您可以自由地共享和演绎本文稿, 只需遵守开源协议[CC By 4.0]
如果这篇文章帮助到你, 可以请我喝一杯咖啡~
这是Effective C++的学习记录。
条款12:关于复制构造函数
保证实例的每一部分都被复制构造
前面提过,如果不希望使用C++的缺省复制构造函数,就需要使用手写的复制构造函数来重载它。比如我希望在每一次调用复制构造函数时输出字符串:
class example{
private:
int val;
public:
example(const example& x)
: val(x.val) {
cout << "Copy construct";
}
};
这种情况看起来没什么问题,而且确实也没什么问题,但是,如果情况变成这样呢?
class example : public base{
private:
int val;
public:
example(const example& x)
: val(x.val) {
cout << "Copy construct";
}
};
请仔细观察上述的程序,找出其中的问题。
很容易发现,example类是一个派生类,但是在它的复制构造函数中貌似未对其基类base做出显式构造,因此编译器会调用基类的缺省构造函数,这就会引发新实例的基类成员与被复制实例的基类成员不同,这是一个致命的问题,会导致我们的复制构造函数发生功能上的问题。这个问题需要引起十足的注意。对此,使用委托构造函数,以下的代码可以解决这个问题:
class example : public base{
private:
int val;
public:
example(const example& x)
: val(x.val), base(x) {
cout << "Copy construct";
}
};
委托构造函数通过派生类x构造了一个基类,作为新实例的基础实例。
这个例子提示我们,在编写复制构造函数时,我们不仅需要考虑派生类的所有成员变量,我们也需要考虑实例的基类。
不使用重载的赋值运算符完成复制构造
使用重载赋值运算符完成复制构造在逻辑上是有错误的。
调用构造函数时,我们的实例还不存在,因此作为实例成员的所谓赋值运算符也是不存在的,而赋值运算符在规定上不拥有构造全部成员的功能。换句话说,赋值运算符只对已构造的实例生效,所以说调用它进行复制是完全错误的。当然有的时候这么写也能过编译,没准儿也会呈现正常的功能,但是请记住,这种做法是不合适的,别试!
同样的,使用复制构造函数来实现赋值运算也是不合适的。试图构造一个已经存在的实例也是不合适的。
但是,很多情况下,赋值重载和复制构造函数有着很大的公共代码,那么基于我们工程师的精益求精原则,我们需要复用它,但是如何复用呢?
一种直观的做法是将公有部分提取出来,写成一个私有成员函数。