第二章 构造/析构/赋值运算符

本文详细阐述了C++中构造函数、析构函数、拷贝构造函数、拷贝赋值运算符的自动生成及手动拒绝,强调了多态基类析构函数应为虚函数的重要性,以及如何避免析构函数引发异常。此外,文章还讨论了在构造函数和析构函数中调用虚拟函数的不当做法,并提出了确保操作符=返回引用、处理自我赋值、复制对象时考虑基类成员的建议。

条款05:了解C++默认编写并调用哪些函数

对于一个空类,编译器会自动为此类生成默认构造函数,拷贝构造函数,析构函数,和拷贝赋值运算符。

例如,如果你的类仅有class Empty{};

但实际上却是:

class Empty{

public

Empty(){......}

Empty(const Empty &rhs) {......}

~Empty() {......}

 

Empty& operator=(const Empty& rhs) {......}

};

以上函数被需要的时候就会被编译器创建出来。如果我们声明了其中的函数,编译器将不再为我们创建默认函数。

更精确的描述是:如果默认产生的函数语法正确,并且被需要,则编译器就会产生默认函数,否则编译器拒绝产生。例如类成员变量中有引用或者常量,由于C++语法不允许引用改变所指对象,所以编译器一定会拒绝默认拷贝赋值运算符。还有一种情况是某个基类将拷贝构造运算符声明为private,则编译器拒绝为其派生类生成一个赋值拷贝运算符。

 

 =================================================================================================================

条款06:若不想使用编译器自动生成的函数,必须明确拒绝

可以把拒绝的函数声明为private,此时即绝了编译器的自动生成,有成功阻止了人们的调用。一般而言这种做法也不是绝对安全,因为成员函数或则友元函数还是可以访问private的,除非你不是定义他们。也可以专门构造一个基类去阻止函数自动生成,然后再去继承他,此时成员函数和友元函数也是无法访问的。

 

 ==================================================================================================================

条款07:为多态基类声明virtual析构函数

为何多态基类的析构函数要为虚函数:因为C++明确指出,当派生类对象经由一个基类指针被删除,而该基类的析构函数是非虚的,其结果是未定义的——实际上执行时通常发生的是对象的派生类对象部分没被销毁。

如果class不含虚函数,通常表示他并不意图被用作基类。此时把析构函数声明为虚函数是个馊主意。

一般只要class中至少有一个虚函数,才为他的析构函数声明为虚函数。

即使class完全不带虚函数,也有可能因为非虚析构函数问题造成资源泄露。如继承自一个标准容器或其他带有非虚析构函数的Class

 

===================================================================================================================== 

条款08:别让异常逃离析构函数

C++不喜欢析构函数突出异常。

如果你的析构函数必须执行一个动作,而该动作可能会失败抛出异常怎么办?

两种方法可以禁止异常离开析构:(1)在发生异常时强制结束程序;(2)记录异常然后跳过;

然而这两种做法都不太好,一个较佳的策略是重新设计类的接口,使用户有机会对可能出现的问题作出反应。即把可能发生异常的部分做成类的成员函数,并添加一个开关(布尔值),然后在析构函数中调用此函数(双保险)。

 

 ===================================================================================================================

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

原因:基类构造期间virtual函数绝对不会下降的派生类阶层。取而代之的是,对象的作为就行隶属base类型一样。非正式的说法:在基类构造期间,虚函数不是虚函数。

对象在派生类构造函数开始之前不会成为一个派生类。

相同的道理也适用析构函数。

由于我们无法使用虚函数从基类向下调用,自构造函数期间,我们可以藉由“令派生类将必要的构造信息向上传递至基类的构造函数”替换加以弥补。

 

=================================================================================================================== 

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

为了实现“连续赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参。(为了保持队形)

 

 ===================================================================================================================

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

一般自我赋值是因为“别名”:所谓别名就是指有一种以上的方法指向某个对象。

阻止自我赋值发生错误的方法:

1)“证同测试”

2)删除之前先复制

3)使用copy and swap技术:

例:widget& widget::operator=const widget& rhs

{

widget temprhs);

swaptemp);

return this

}

 

==================================================================================================================== 

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

不要忘记基类的成员函数。

下面是一个注意事项:令拷贝赋值操作符调用拷贝构造函数是不合理的,反过来同样不合理。如果两者真的很相近,我们可以创建一个新的成员函数给他们调用。为何不合理,原因是:构造函数调用拷贝赋值操作符,就像一个尚未初始化的对象做了“只对已初始化对象才有意义”的时。反过来则是试图再次构造一个已经存在的对象。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值