Effective C++ 第二章

2. Constructors, Destructors, and Assignment Operators 构造/析构/赋值运算

05: Know what functions C++ silently writes and calls

了解C++默默编写并调用哪些函数

  • 编译器可以暗自为class创建默认构造函数、复制构造函数、赋值运算操作符和析构函数
    空类(empty class)不是真正的空类,当有需要被调用以上函数时,编译器会默认创建

    class Empty { };
    // 相当于
    class Empty {
    public:
    	Empty() { ... }
    	Empty(const Empty& rhs) { ... }
    	~Emtpy( ) { ... }
    	Empty& operator=(const Empty& rhs) { ... }
    };
    

06: Explicitly disallow the use of compiler-generated functions you do not want

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

  • 希望设计的类不支持以下操作

    HomeForSale h1;
    HomeForSale h2;
    HomeForSale h3(h1); // 不能通过编译
    h2 = h1;			// 也不能通过编译
    
  • 将拷贝构造函数、赋值运算操作符声明为private,阻止了编译器默认创建public版本
    并不绝对安全,无法阻止成员函数和友元函数调用

  • 将成员函数声明为private且不实现,如C++ iostream的设计

    class HomeForSale {
    public:
    		...
    private:
    	HomeForSale(const HomeForSale&);			// 只有声明,没有定义
    	HomeForSale& operator=(const HomeForSale&);
    }
    

    在成员函数和友元函数中调用,能通过编译,但不能链接,连接器会报错

    • 在编译阶段避免链接期错误,设计一个专门阻止拷贝动作的基类,HomeForSale继承基类
    class Uncopyable {
    protected:
    		Uncopyable() { }
    		~Uncopyable() { }
    private:
    		Uncopyable(const Uncopyable&);
    		Uncopyable& operator=(const Uncopyable&);
    };
    
    class HomeForSale : private Uncopyable {    // 不再声明 拷贝构造函数和赋值运算操作符
    	...
    };
    

    这里使用了private继承,public继承是is a关系,两者使用注意事项详见32和39。

07: Declare destructors virtual in polymorphic base classes

为多态基类声明virtual析构函数

  • 多态的基类应该声明一个virtual析构函数;如果类带有任何virtual函数,也应该拥有一个virtual析构函数
  • 类的设计目的不是作为基类使用,也不是为了具备多态性,则不应该声明virtual析构函数
    原因:
    实现虚函数的对象,包含了虚函数表指针(virtual table pointer),vptr指向一个由函数指针构成的数组
    • 对象的体积会增加(32位计算机上为4字节,64位计算机上为8字节)
    • 和其他语言(如C语言)不再具有移植性,除非额外实现
  • 所有的STL容器带有non-virtual析构函数,如vector,list,set等,避免继承一个标准容器或其他带有non-virtual析构函数的类
  • 纯虚函数不能被实例化

08: Prevent exceptions from leaving destructors

别让异常逃离析构函数

  • 在析构函数中加入 try catch,吞下异常
  • 提供新的函数关闭连接供客户使用

09: Nevel call virtual functions during construction or destruction.

绝不在构造和析构过程中调用virtual函数

  • 构造和析构函数不会调用到子类的虚函数

10: Have assignment operators return a reference to *this

令 operator= 返回一个 reference to *this

  • 实现了连续复制的语义:
    x = y = z = 1; ==> x = (y = (z = 1));

11: Handle assignment to self in operator=.

在operator= 中处理自我赋值

  • 比较来源对象和目标对象的地址
     Widget& Widget::operator=(const Widget& rhs) {
       if (this == &w) return *this;
       delete pb;
       pb = new Bitmap(*rhs.pb);
       return *this;
     }
    
  • 精心周到的语句顺序
     Widget& Widget::operator=(const Widget& rhs) {
       Widget *pOrig = pb;
       pb = new Bitmap(*rhs.pb);
       delete pOrig;
       return *this;
     }
    
  • copy and swap技术:使用拷贝构造函数生成副本cp,交换this与cp,返回this

12: Copy all parts of an object.

复制对象时勿忘其每一个成分

  • 新增数据成员,需要修改拷贝构造函数、赋值运算符
  • 继承子类的拷贝函数确保复制了所有父类成分
  • 拷贝构造函数和赋值运算操作符的共同部分,应该放入统一的函数,而不是尝试互相调用
原文地址:https://www.cnblogs.com/izcat/p/13557231.html