(整理自Effctive C++,转载请注明。整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/)
编译器会在必要时候为我们的classes创建copying函数,这些“编译器生成版”的行为:将被烤对象的所有成员变量都做一份拷贝。
如果你声明自己的copying函数,意思就是告诉编译器你并不喜欢缺省实现中的某些行为。编译器仿佛被冒犯似的,会以一种奇怪的方式回敬:当你的实现代码几乎必然出错时却不告诉你。所以自己实现copying函数时,请遵循一条规则:如果你为class添加一个成员变量,你必须同时修改copying函数(你也需要修改class的所有构造函数以及任何非标准形式的operator=)。如果你忘记,编译器不太可能提醒你。
但是也要注意,一旦发生继承,可能会造成此一主题最暗中肆虐的一个潜藏危机。试考虑:
1: class PriorityCustomer:public Customer{
2: public:
3: ...
4: PriorityCustomer( const PriorityCustomer& rhs) ;
5: PriorityCustomer& operator=( const PriorityCustomer& rhs ) ;
6: ...
7: private:
8: int priority ;
9: };
10:
11: PriorityCustomer::PriorityCustomer( const PriorityCustomer& rhs )
12: :priority(rhs.priority)
13: {
14: logCall("PriorityCustomer copy constructor" ) ;
15: }
16:
17: PriorityCustomer& PriorityCustomer::operator= ( const PriorityCustomer& rhs )
18: {
19: logCall("PriorityCustomer copy assignment operator ");
20: priority = rhs.priority ;
21: return *this ;
22: }
我们可以看到,PriorityCustomer的copying函数看起来好像复制了PriorityCustomer的每一样东西。是的,它们复制了PriorityCustomer声明的成员变量,但每个PriorityCustomer还内含它所继承的Customer成员变量复件,而那些成员变量却未被复制。PriorityCustomer的copy构造函数并没有指定实参传给其base class构造函数,因此PriorityCustomer对象的Customer成分会被不带实参之Customer构造函数(即default构造函数---必定有一个否则无法通过编译)初始化。default构造函数对base class 成分执行缺省的初始化动作。
以上事态在PriorityCustomer的copy assignment操作符身上只有轻微不同。它不曾企图修改其base class的成员变量,所以那些成员变量保持不变。
任何时候只要你担负起“为derived class撰写copying函数”的重大责任,必须很小心地赋值其base class成分。那些成分往往是private,所以你无法直接访问它们,你应该让derived class的copying函数调用相应的base class函数:
1: PriorityCustomer:PriorityCustomer( const PriorityCustomer& rhs )
2: :Customer(rhs), //调用base class的copy构造函数
3: priority(rhs.priority)
4: {
5: logCall( "PriorityCustomer copy constructor " ) ;
6: }
7:
8: PriorityCustomer&
9: PriorityCustomer::operator=( const PriorityCustomer& rhs )
10: {
11: logCall("PriorityCustomer copy assignment operator") ;
12: Customer::operator=(rhs) ; //对base class成分进行赋值动作
13: priority = rhs.priority ;
14: return *this ;
15: }
本条款题目所说的“复制每一个成分”现在应该说的很清楚了。当你编写一个copying函数,请确保(1)复制所有local成员变量,(2)调用所有base class内适当的copying函数。
另外,如果你发现你的copy构造函数和copy assignment操作符有相近的代码,消除重复代码的做法是,建立一个新的成员函数供两者调用。这样的函数往往是private且常被命名为init。这个策略可以安全消除copy构造函数和copy assignment操作符之间的代码重复。
不要令某个copying函数调用另一个copying函数:令copy assignment操作符调用copy构造函数是不合理的,因为这试图构造一个已经存在的对象。反方向—令copy构造函数调用copy assignment操作符—同样无意义。构造函数用来初始化新对象,而assignment操作符只施行于已初始化对象身上。
请记住:
(1)Copying函数应该确保复制“对象内的所有的成员变量”及“所有base class成分”。
(2)不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放在第三个函数中,并有两个copying函数共同调用。