Effective C++笔记(二)

二:构造/析构/赋值运算
条款05:了解C++默默编写并调用哪些函数
编译器产出的析构函数是个non-virtual,除非这个class的base class自身声明有virtual析构函数

请记住:编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。

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

//方法一:将copy构造函数、copy assignment操作符声明为private并且不定义。
class HomeForSale{
public:
    ...
private:
    ...
    HomeForSale(const HomeForSale);
    HomeForSale &operator=(const HomeForSale &);
};
//方法二:将连接期错误移至编译期,专门设计为了阻止copying动作的base class
class Uncopyable{
public:
    Uncopyable(){}  //允许derived对象构造和析构
    ~Uncopyable(){}
private:
    ...
    Uncopyable(const Uncopyable); //但阻止copying
    Uncopyable&operator=(const Uncopyable&);
};
class HomeForSale : private Uncopyable{
}
//只要任何人尝试拷贝HomeForSale对象,编译器便试着生成一个copy构造函数和一个copy assignment操作符,
//这些函数的“编译器生成版”会尝试调用其base class的对应兄弟,那些调用会被编译器拒绝,因为其base class的拷贝函数是private

请记住:为驳回编译器自动提供的机能,可将相应的成员函数声明为private并且不予实现。使用Uncopyable这样的base class也是一种做法。

条款07:为多态基类声明virtual析构函数
当derived class 对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义------实际执行时通常发生的是对象的derived成分没被销毁,造成一个“局部销毁”对象。
解释:①当基类的析构函数声明不是虚函数时,因为delete p调用的是声明类型(即基类)的析构函数,所以只能销毁基类对象而无法销毁派生类对象。②当基类的析构函数声明为虚函数,那么派生类的析构函数也是虚函数,此时调用delete p时发生动态绑定,运行时会根据实际类型调用该对象的虚函数。

任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数(是因为他会作为基类吗?错,作为基类不一定是为了多态用途)。

请记住:1.polymorphic(带多态性质的)base class应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该拥有一个virtual析构函数。
2.Classes的设计目的如果不是为了base classes使用,或不是为了具备多态性,就不该声明virtual析构函数。

条款08:别让异常逃离析构函数
请记住:1.析构函数绝对不要吐出吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。
2.如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class 应该提供一个普通函数(而非在析构函数中)执行该操作。

条款09:绝不在构造和析构过程中调用virtual函数
对象在derived class构造函数开始执行前不会成为一个derived class对象(先调用基类构造函数)。
在构造和析构过程中不要调用virtual函数,因为这类调用调用从不下降至derived class(此时调用这个virtual函数的是基类,即使目前即将建立的对象类型是derived class)。在构造期间可以藉由“令derived class 将必要的构造信息向上传递至base class 构造函数”,替换之而加一弥补。p51

请记住:在构造和析构过程中不要调用virtual函数,因为这类调用调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)。

条款10:令operator = 返回一个 reference to* this
可以实现赋值连锁性质 x = y = z = 15;
请记住:令operator = 返回一个 reference to* this.

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

建立一个class用来保存一个指针指向一块动态分配的位图(bitmap)
class Bitmap{...};
class Widge{
void swap(Widge &rhs);//交换*this 和rhs的数据
private:
	Bitmap* pb;
};
 //证同测试达到“自我赋值”的检验目的:if(this == &rhs) return *this;
 	//错误版本,“在停止使用资源之前意外释放了他”
 	 Widge & Widge::operator = (const Widge& rhs){
        delete pb;
        pb = new Bitmap(*rhs.pb);
        return *this;
    }
    //正确版本 
    Widge & Widge::operator = (const Widge& rhs){
        Bitmap *pOrig = pb;//记住原先的pb
        pb = new Bitmap(*rhs.pb);//令pb指向*pb的一个副本
        delete pOrig; // 删除原来的pb
        return *this;
    }
    //正确版本 copy-and-swap
     Widge & Widge::operator = (const Widge& rhs){
        Widge temp(rhs);
        swap(temp);
        return *this;
    }

请记住:1.确保当对象自我赋值时operator = 有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
2.确定任何函数如果操作一个以上的对象,而其中多个对象是是同一个对象时,其行为仍然正确。

条款12:复制对象时勿忘其每一个成分
当你编写一个copying函数确保①复制所有local成员变量②调用所有base class内的适当的copying函数。(②这些成分是private,无法直接访问他们,调用适当的base class函数)。
请记住:1.Copying函数应该确保复制“对象内的所有成员变量”及“所有base class 成分”。
2.不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二零二三.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值