周末闲着没事,写点以前的东西记录一下。
对象的不同使用方式会导致拷贝构造函数在不同时期进行调用,理解拷贝构造函数的调用时机有助于我们认识程序运行机制,帮助我们编写更高效的程序。
直接上代码:
#include <iostream> using namespace std;
class copy { public: copy(){ cout<<"I am constructor-01."<<endl; } copy(int a) { cout<<"I am constructor-02."<<endl; _a = a; } copy(int a, int b) { cout<<"I am constructor-03."<<endl; _a = a; _b = a; } copy(const copy &obj){ cout<<"I am copy constructor."<<endl; _a = obj.a; } ~copy(){ cout<<"I am destructor."<<endl; } void printf(){ printf("a : %d, b : %d \n", _a, _b); } int getA(){ return _a; } int getB(){ return _b; } private: int _a; int _b; };
void fuc_a(copy p){ Cout << “fuc : a=” << getA() << “, b=” << getB() << endl; }
void fuc_b(){ copy a(1, 2); return a; }
//拷贝构造函数调用场景1 void callTime01(){ copy a1; copy a2 = a1; //定义变量并初始化,用对象a1初始化对象a2 a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy }
//拷贝构造函数调用场景2 void callTime02(){ copy a1(10); copy a2(a1); a2 = a1; }
//拷贝构造函数调用场景3 |
void callTime03(){ copy a1(1, 2); fun(a1); }
//拷贝构造函数调用场景4 Void callTime04(){ fun_b(); // #1 // 方式一 copy a; a = fun_b(); //#2 // 方式二 copy b = fun_b(); // #3 } |
好了,大家有没有发现以上四种构造函数的调用场景有什么不同?
下面开始进行逐一分析:
方法一:
该方法是普通的对象定义方法,用一个对象初始化或者赋值给另外一个对象,拷贝构造函数在这个过程被调用;注意copy a2 = a1和a2=a1是两个不同的方法,copy a2 = a1是利用a1来初始化a2,调用的是类的默认拷贝构造函数;a2=a1是等号法,类似a1赋给a2,调用的是类默认“=”操作符,两种有根本的不同,但是他们都是类默认的函数或操作符,都属于钱拷贝。
方法二:
该方法和方法一基本相同,区别在于对应定义并初始化的过程方法一采用等号法,方法二采用等号法。
方法三:
主要分析fun(a1)这个函数调用会产生什么结果?会调用多少次析构函数?答案是两次。为什么?首先,fun(a1)会使用参数a1区初始化形参p,此时会调用拷贝构造函数用a1初始化p,fun返回后马上调用析构函数析构p;当fun(a1)执行完成退出函数后,a1的生命周期结束,再次调用析构函数析构a1,因此会有两次析构。
方法四:
#1中的fun_b()会产生什么结果?
fun_b()中返回了局部变量a,我们都知道函数中是不能返回局部对象的,此时函数会产生一个匿名对象返回给调用者,而对a调用析构函数进行释放;另外调用fun_b()之后并没有使用,因此会再一次调用析构函数释放匿名对象,因此#1会调用两次析构函数。如果不想马上释放匿名函数可以使用另外一个对象去接收该匿名函数,而接收方法主要有两种:
方式1:
如#2,首先定义个对象a,然后再把匿名对象赋给a,之后会析构匿名对象。
方式2:
如#3,直接利用匿名对象初始化接收对象b,此时匿名对象转换为b,不会析构匿名对象,因此该方式会比方式1更高效。
结论:fun_b()返回的是一个元素,是一个新对象类的拷贝构造函数,如果是赋值给另外一个同类型的对象,匿名对象被析构;如果是用它来初始化另外一个同类型的对象,匿名对象转成有名对象。
好了,到此对拷贝构造函数调用的四种应用场景分析完毕。