有的看着理所当然的东西往往被我们忽略其深层的意义,就像c++ 拷贝构造函数的参数为什么必须是引用类型呢,许多初学者都会说,“为了减少一次内存复制呗!”(其实刚开始我也是这样想的),但究竟是不是这样呢?来,看个小例子(你一定会说:昂....原来是这样啊!);
#include<iostream> using namespace std; class myExample { int mTest; public: myExample(int x):mTest(x){ //带参数的构造函数 cout << "我是构造函数!"<<endl; } myExample(const myExample &ex){ //拷贝构造函数 mTest=ex.mTest; cout <<"我是拷贝构造函数!"<<endl; } myExample& operator=(const myExample &ex){ //赋值函数(赋值运算符重载) cout << "赋值运算符重载" <<endl; mTest=ex.mTest; return *this; } void myTestfun(myExample ex){ } }; int main(int argc,char *argv[]) { myExample a(2); myExample b(3); b=a; myExample c=a; b.myTestfun(a); return 0; }
看结果:
我是构造函数! //myExample a(2); 我是构造函数! //myExample b(3); 赋值运算符重载 //b=a; 我是拷贝构造函数! //myExample c=a; 我是拷贝构造函数! //b.myTestfun(a);
如果你想的结果和上面的一样,恭喜你,你不用往下看了.
我们来分析一下:
第一个输出和第二个输出:调用构造函数,就不用解释了吧!
第三个和第四个为什么不一样呢?大家仔细看,b已经被实例化了,不需要构造,在这里,只是把a的值赋给它,只会调用赋值函数,而第四个c还没有被实例化,因此调用拷贝构造函数,构造处c,而不是赋值,
第五个实际上是把a作为参数传递给myTestfun(myExample ex),就相当于myExample ex=a;所以调用拷贝构造函数.
通过这个例子,我们来分析一下,拷贝构造函数为什么必须用引用类型,myExanple c=a;当我们不用引用作为拷贝构造函数的形参时,使得a通过值传递传递给c,因为要构造对象, 又会调用拷贝构造函数,就这样一直递归调用递归下去了.
所以绕了那么大的弯子,就是想说明拷贝构造函数的参数使用引用类型不是为了减少一次内存拷贝, 而是避免拷贝构造函数无限制的递归下去。
大家在想想,既然能设计为引用类型呢,那也就能设计成指针类型啊, 那为什么不呢,唯一区别点就在于效率吧!
如果形参是指针类型的,从编译的角度看:
程序在编译时分别将指针和引用添加到符号表上, 符号表上记录的是变量名及变量所对应的地址,指针在符号表上对应的地址是指针变量的地址值,指针变量中存的才是对应指向对象的地址值,而引用在符号上对应的地址值为引用的对应的地址值.在这里, 指针就像中介,我们租房子跟主家谈和跟中介谈,哪个划算,大家应该清楚吧!