1 #include <iostream.h> 2 3 class CExample 4 { 5 int m_nTest; 6 public: 7 8 CExample(int x):m_nTest(x) //带参数构造函数 9 { 10 cout << "constructor with argument/n"; 11 } 12 13 CExample(const CExample & ex) //拷贝构造函数 14 { 15 m_nTest = ex.m_nTest; 16 cout << "copy constructor/n"; 17 } 18 19 CExample& operator = (const CExample &ex)//赋值函数(赋值运算符重载) 20 { 21 cout << "assignment operator/n"; 22 m_nTest = ex.m_nTest; 23 return *this; 24 } 25 26 void myTestFunc(CExample ex) 27 { 28 } 29 }; 30 31 int main() 32 { 33 CExample aaa(2); 34 CExample bbb(3); 35 bbb = aaa; 36 CExample ccc = aaa; 37 bbb.myTestFunc(aaa); 38 39 return 0; 40 }
(1)
这个例子的输出结果:
constructor with argument //CExample aaa(2);
constructor with argument //CExample bbb(3);
assignment operator //bbb = aaa;
copy constructor //CExample ccc = aaa;
copy constructor // bbb.myTestFunc(aaa);
第3个和第4个输出结果不同的原因是: bbb对象已经实例化了,不需要构造,此时只是将aaa赋值给bbb,只会调用赋值函数, 但是ccc还没有实例化,因此调用的是拷贝构造函数,构造出ccc,而不是赋值函数。
第5个输出结果的原因和第4个一致。
通过这个例子, 我们来分析一下为什么拷贝构造函数的参数只能使用引用类型。
看第四个输出: copy constructor //CExample ccc = aaa;
构造ccc,实质上是ccc.CExample(aaa); 我们假如拷贝构造函数参数不是引用类型的话, 那么将使得 ccc.CExample(aaa)变成aaa传值给ccc.CExample(CExample ex),即CExample ex = aaa,因为 ex 没有被初始化, 所以 CExample ex = aaa 继续调用ex的拷贝构造函数,也就是 ex.CExample(aaa),必然又会有aaa传给CExample(CExample ex), 即 CExample ex = aaa;那么又会触发拷贝构造函数,就这下永远的递归下去。但此处ex如果是引用的的话,即ex就已经被实例化了,也就不会一直反复调用其拷贝构造函数了。
所以绕了那么大的弯子,就是想说明拷贝构造函数的参数使用引用类型不是为了减少一次内存拷贝, 而是避免拷贝构造函数无限制的递归下去。
(2) 接下来,我们再测试一下赋值构造函数的参数,如果我们把它的参数也改为值传递,做一个测试。
1 class test 2 { 3 public: 4 test() 5 { 6 cout << "constructor with argument "; 7 } 8 ~test() 9 { 10 } 11 test(test& t) 12 { 13 cout << "copy constructor "; 14 } 15 test&operator=(test e) 16 { 17 cout << "assignment operator "; 18 return *this; 19 } 20 }; 21 int _tmain(int argc, _TCHAR* argv[]) 22 { 23 test ort; 24 test a(ort); 25 test b = ort ; 26 a = b; 27 return 0; 28 }
输出为:
总结:
拷贝构造函数的参数必须为引用。赋值构造函数参数既可以为引用,也可以为值传递,值传递会多一次拷贝。因此建议赋值构造函数建议也写为引用类型。