拷贝构造函数:拷贝构造函数就是在用一个类对象来建立另一个类对象时被调用的构造函数,若是咱们没有显示的提供拷贝构造函数,编译器会隐式的提供一个默认拷贝构造函数。ios
拷贝构造函数的定义是X(const X& ){}函数
1 class Test{ 2 int m_i; 3 public: 4 Test(int i):m_i(i){} 5 Test(const Test& vt):m_i(vt.m_i){}//拷贝构造函数
int getI()const {return m_i;}
6 7 };
1 int main(){ 2 3 Test t(12); 4 Test t2(t);//调用拷贝构造函数来初始化t2对象 5 Test t3=t;//等价于t3(t),也是调用拷贝构造函数 6 return 0; 7 }
记住拷贝构造函数的只有一个参数,而且这个参数是类类型的const引用,参数不能是普通的值传递,必须是引用。缘由有二测试
一:若是参数是const Test vt,那么实参在传递给形参的时候,仍是会给形参建立对象,分配内存。这时把实参传递给形参,仍然是是用类对象初始化类对象,仍是会调用拷贝构造函数,就会造成递归调用。spa
二:若是传递引用,其实是传递的一个地址,不会建立新的对象,而拷贝时也只是拷贝的一个地址大小的空间,会增长程序的效率。code
当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新创建一个局部对象,并把实参拷贝到新的对象中。理所固然也调用拷贝构造函数。对象
1 void fun(Test vt){ 2 3 cout<<vt.,getI()<<endl; 4 5 }
这个函数的参数是类的对象,会调用拷贝构造函数的blog
1 int main(){ 2 3 Test t(12); 4 Test t2 = t;//调用一次拷贝构造函数 5 Test t3(t);//调用一次拷贝构造函数 6 fun(t);//调用拷贝构造函数 7 return 0; 8 }
fun的参数变成对象的引用时,就不会再调用拷贝构造函数了。递归
返回值为Const 引用 以及类对象的区别生命周期
当函数的返回值是类对象,函数执行完成,返回到调用者时也会调用拷贝构造函数。理由也是要创建一个临时对象中,从函数返回的对象来初始化产生的临时对象。内存
1 //test.h 2 #ifndef TEST_H 3 #define TEST_H 4 class Test 5 { 6 int m_i; 7 public: 8 Test(int i=0); 9 Test(const Test& vt); 10 ~Test(); 11 int getI()const; 12 }; 13 #endif //TEST_H 14 15 16 //test.cpp 17 #include "Test.h" 18 #include<iostream> 19 using std::cout; 20 using std::endl; 21 22 Test::Test(int i) :m_i(i) 23 { 24 cout << "default constructor" << endl; 25 } 26 27 Test::Test(const Test& vt):m_i(vt.m_i){ 28 cout << "copy constructor" << endl; 29 } 30 31 Test::~Test() 32 { 33 } 34 35 int Test::getI()const{ 36 return m_i; 37 } 38 39 //demo.cpp 40 41 #include<iostream> 42 #include"Test.h" 43 using std::endl; 44 using std::cout; 45 void fun1(Test& vt){ 46 47 cout << "m_i=" << vt.getI() << endl; 48 } 49 void fun2(Test vt){ 50 cout << "m_i=" << vt.getI() << endl; 51 } 52 53 Test fun3(const Test& vt){ 54 return vt; 55 } 56 int main(){ 57 58 Test t; 59 fun3(t);//由于fun3函数的参数是类的引用,所以不用产生临时对象。可是返回值类型为类对象会产生临时对象, 60 // 因此要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中 61 return 0; 62 }
咱们还能够在析构函数中添加代码,来测试临时对象时何时释放的。
在析构函数~Test()中添加打印代码,main函数以下所示:
1 int main(){ 2 3 Test t; 4 fun3(t);//由于fun3函数的参数是类的引用,所以不用产生临时对象。可是返回值类型为类对象会产生临时对象, 5 // 因此要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中 6 cout << "........." << endl; 7 return 0; 8 }
咱们能够看到,临时对象产生后,若是没有别的对象接受,立刻就销毁。若是有别的对象接受,再复制完成后销毁。可是若是mia函数中添加一句以下的代码
1 int main(){ 2 3 Test t; 4 Test t2=fun3(t);//由于fun3函数的参数是类的引用,所以不用产生临时对象。可是返回值类型为类对象会产生临时对象, 5 // 因此要调用一个拷贝构造函数,把从函数返回的值,拷贝到生成的临时对象中 6 cout << "........." << endl; 7 return 0; 8 }
此时临时对象被Test t2接受,就没有立刻释放,而是当t2的生命周期完时释放。
如今增长一个函数fun4,它的返回值类型和传递的参数类型都是类的引用
1 const Test& fun4(const Test& vt){ 2 return vt; 3 }
而后把main的代码修改为以下的;
1 int main(){ 2 3 Test t;//建立t对象,调用默认构造函数 4 const Test& t1=fun4(t);//传参,和返回值都没有产生临时对象。 5 cout << "........." << endl; 6 return 0; 7 }
运行结果
咱们能够看到,当返回值为类的引用时,并不会产生临时对象。这是一个很是好的改变程序效率的方法。
因此从上面的一系列代码中,咱们能够看到,传参和返回值类型会影响程序的效率,由于传参类型是值类型的话,会产生临时对象,临时对象的产生,释放,拷贝,都会产生消耗,从而致使程序效率下降。