第十七篇:复制控制( 上 ) --- 自定义复制函数
前言
若要将a对象复制给b对象,编译器应当做何工作?C++初学者也许会直接说” a对象的成员复制给b对象的成员 “。在很多情况下,这种说法正确,事实上C++会给类定义一个默认的复制函数,它所做的工作也正是如此。但,下面问题来了:如果类的成员当中有指针,这种做法还行吗?本文将对这个问题作出实例分析。
一个典型错误示例
下面这个代码示例用来检验前言中所提到的问题:
1 #include <iostream> 2 #include <cstdlib> 3 #include <fstream> 4 #include <string> 5 6 using namespace std; 7 8 class A { 9 public: 10 // 构造函数为指针成员开辟空间并赋初值0 11 A() { 12 num_p = new int; 13 *num_p = 0; 14 } 15 // 给指针所指对象赋值 16 void setNum(int num) { 17 *num_p = num; 18 } 19 // 获取指针所指对象 20 int getNum() const { 21 int num = *num_p; 22 return num; 23 } 24 private: 25 int *num_p; 26 }; 27 28 int main() 29 { 30 A a1; 31 32 // 设置a1指针成员所指对象的值 33 a1.setNum(1); 34 // 调用系统自动合成的复制函数 35 A a2 = a1; 36 // 观察得出a1,a2的指针成员所指对象均为整数1。 37 cout << "a1`s num: " << a1.getNum() << endl; 38 cout << "a2`s num: " << a2.getNum() << endl; 39 40 // 修改a1指针成员所指对象的值 41 a1.setNum(2); 42 // 观察得出不单a1的指针成员所指对象改了,a2的也跟着变了。 43 cout << "a1`s num: " << a1.getNum() << endl; 44 cout << "a2`s num: " << a2.getNum() << endl; 45 46 return 0; 47 }
运行结果:
我们可以观察到,当类中具有指针成员时,如果使用默认的复制函数,那么此后当某个对象修改了其指针成员所指对象,那么该类派生的其他对象的指针成员所指对象也会跟着改变。这显然不符合编程规范,下面我们将提出解决方案。
解决思路
既然系统自带的复制函数无法满足我们的要求,那么我们可以自定义一个以指定编译器在复制对象时的操作。
一个正确示例
下面这个代码给出了一个具体解决方案,复制函数启用时,拷贝指针所指对象,而不是指针本身:
1 #include <iostream> 2 #include <cstdlib> 3 #include <fstream> 4 #include <string> 5 6 using namespace std; 7 8 class A { 9 public: 10 // 构造函数为指针成员开辟空间并赋初值0 11 A() { 12 num_p = new int; 13 *num_p = 0; 14 } 15 // 自定义复制函数 16 A(const A & a) { 17 num_p = new int; 18 *num_p = a.getNum(); 19 } 20 // 给指针所指对象赋值 21 void setNum(int num) { 22 *num_p = num; 23 } 24 // 获取指针所指对象 25 int getNum() const { 26 int num = *num_p; 27 return num; 28 } 29 private: 30 int *num_p; 31 }; 32 33 int main() 34 { 35 A a1; 36 37 // 设置a1指针成员所指对象的值 38 a1.setNum(1); 39 // 调用自定义的复制函数 40 A a2=a1; 41 // 观察得出a1,a2的指针成员所指对象均为整数1。 42 cout << "a1`s num: " << a1.getNum() << endl; 43 cout << "a2`s num: " << a2.getNum() << endl; 44 45 // 修改a1指针成员所指对象的值 46 a1.setNum(2); 47 // 观察得出a1的指针成员所指对象改了,a2的没变。 48 cout << "a1`s num: " << a1.getNum() << endl; 49 cout << "a2`s num: " << a2.getNum() << endl; 50 51 return 0; 52 }
运行结果:
这一次,两个对象没有出现“ 相互干扰 ”了。
说明
1. 复制函数不单在A a = b的形式下启用,在作为函数参数传递时,生成容器时也会启用,这点要注意到。
2. 注意复制函数的原型( 形参为const引用且无返回 )