《Effective C++》(总结笔记) 章节二(下):构造/析构/赋值运算 Constructors,Destructors,and Assignment Operators

文章讨论了C++编程中的几个关键点:在构造和析构过程中不应调用虚函数,以避免多态失效;赋值运算符应返回对*this的引用,支持连锁赋值;处理operator=中的自我赋值情况,防止数据丢失;拷贝对象时确保所有成员和基类成分都被正确复制。这些最佳实践有助于编写更安全和高效的代码。
摘要由CSDN通过智能技术生成

目录

条款09:绝不在构造和析构过程中调用 virtual函数

Never call virtual functions during construction or destruction.

条款10:令 operator=返回一个reference to *this

Have assignment operators return a reference to *this.

条款11:在 operator=中处理“自我赋值”

Handle assignment to self in operator=.

条款12:复制对象时勿忘其每一个成分.

Copy all parts of an object.


条款09:绝不在构造和析构过程中调用 virtual函数
Never call virtual functions during construction or destruction.

看下面的代码:

构造BuyTransaction对象时会先调用Transaction的构造函数,而基类构造函数调用了虚函数,这时候不会实现多态,会调用父类的logTransation函数,原因很明显,要实现多态需要派生类对象有虚函数指针成员,而派生类对象还没构造,自然不会调用派生类的logTransation函数。也可以这么理解:在基类构造期间,派生类的类型是基类的类型。

        同样的道理也适用于析构函数,一旦进入了基类的析构函数,派生类成员变量早就被析构了,变成为未定义的值,这时其类型就是基类类型。

应该怎么做?

在子类构造函数构造父类成分的时候,传递参数给基类的构造函数。基类构造函数传递参数给non-virtual函数调用:

总结:构造和析构期间不要调用虚函数,因为此时无法实现多态,可以用传递参数的方式调用非虚函数。 

条款10:令 operator=返回一个reference to *this
Have assignment operators return a reference to *this.

赋值表达式可以写成连锁形式,比如:x=y=z=15;

这是因为式子会被解析成:x=(y=(z=15)));

对于对象而言也是一样的,注意要返回对象的引用类型,赋值运算符重载返回 * this。

比如:

class Widget{
public:
    Widget& operator=(const Widget&w)
    {
        ...
    }
};

总结:让类的赋值相关的运算符,返回*this的引用。 

条款11:在 operator=中处理“自我赋值”
Handle assignment to self in operator=.

看下面代码:

 看似代码没有问题,其实是有安全问题的,因为当Widget对象进行自我赋值的时候,会将自己的pb指向的内容释放掉,然后持有一个指针指向一个已被删除的对象,正确的代码是:

也可以让代码具有异常安全性,就没有自我赋值的问题了,方法就是先拷贝再释放指针:

 但注重效率问题的话,还是得进行自我赋值的判断,避免做没必要的拷贝工作。

一种简单的替代方案是使用copy and swap技术,直接看代码:

这种写法还能更简洁,就是将拷贝的工作直接交给参数:

 总结:确保当对象自我赋值时不会发生问题,可以使用判断对象地址与this的关系、先拷贝再释放或copy and swap技术来实现。

条款12:复制对象时勿忘其每一个成分.
Copy all parts of an object.

看下面代码:

 目前代码还正常,但是当新加入一个成员后:

这意味着需要修改所有构造和拷贝函数,来处理新加入的成员,因为用户自定义了函数,编译器不会帮我们拷贝新加入的成员。

一旦发生继承,可能会导致更严重的问题,比如下面代码:

 派生类PriorityCustomer好像拷贝了所有成员,实际上它所继承的Customer成员变量都没有拷贝下来,因为在构造函数初始化列表没有提到过Customer,结果就是PriorityCustomer对象的Customer的成分被Customer的无参的构造函数初始化(即默认构造函数,没有就编译报错)。

正确的做法是让派生类的拷贝函数调用相应的基类构造函数:

 另外注意不能让拷贝构造函数调用赋值函数,反之也不行,因为无意义。当发现拷贝构造函数和赋值函数有相近的代码,正确的做法通常是写一个private的init函数给两者调用,避免代码重复。

总结:

拷贝函数确保拷贝了所有的成员变量和所有的基类成分。

不要以一个拷贝函数实现另一个拷贝函数,应当提取公共代码写成第三个函数供它们调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值