zoukankan      html  css  js  c++  java
  • 构造/析构/赋值运算

    一、C++默认编写的函数##

    如果类中没有定义,程序却调用了,编译器会产生一些函数:

    1. default 构造函数
    2. copy 构造函数
    3. copy assignment 操作符
    4. 析构函数(non virtual)

    所以写下:

    class Empty { };
    

    就好比写下:

    class Empty {
    public:
        Empty(){ ... }
        Empty(const Empty& rhs) { ... }
        ~Empty() { ... }
        
        Empty& operator=(const Empty& rhs) { ... }
    };
    
    • 这些函数都是public且inline。
    • 惟有当这些函数被需要(被调用),它们才会被编译器创建出来。
    • 如果自己构造了构造函数(无论有无参数),编译器不会产生default构造函数。
    • base class如果把拷贝构造函数或者赋值操作符设置为private,不会产生这两个函数。
    • 含有引用成员变量或者const成员变量不产生赋值操作符。
    • 编译器产生的析构函数是个non-virtual。

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

    二、拒绝编译器自动生成函数##

    构造函数与析构函数是每一个类必须有的,但copy构造函数和copy assignment操作符对于某些类是不需要的需要明令禁止。
    为了避免编译器自动生成copy构造函数和copy assignment操作符可以将其声明为private成员:

    class Uncopyable{
    private:
        Uncopyable(const Uncopyable&);
        Uncopyable& operator= (const Uncopyable&);
    }
    

    但类中成员函数和友元函数还是可以调用private函数,所以可以专门设计防止拷贝动作的基类(Boost也提供了这个类,名为noncopyable):

    class nocopy : private Uncopyable {
    ...
    }
    

    派生类不再声明copy构造函数和copy assignment操作符。
    如果不需要copy构造函数和copy assignment操作符要明令禁止,为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像noncopyable这样的基类也是一种做法。

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

    • 1.给多态基类应该主动声明virtual析构函数
      如果多态基类析构函数没有加声明为virtual,当指向派生类对象的基类指针调用delete的时候,基类成分通常会被销毁,而派生类的成分可能还留在堆里。这可是形成资源泄漏、败坏之数据结构、在调试器上消费许多时间。

    • 2.非多态基类,不要声明virtual析构函数
      当类不企图被当做基类的时候,不要令其析构函数为virtual。因为实现virtual函数,需要额外的开销(指向虚函数表的指针vptr)。

    多态基类一般包含一个virtual函数,所以一个类里面有virtual函数则声明virtual析构函数,没有virtual则不声明virtual析构函数。

    四、构造函数和析构函数中不调用virtual函数##

    调用派生类构造函数时会先调用基类构造函数,在派生类对象的基类构造期间,对象的类型会被编译器解析为基类而不是派生类。此时如果调用virtual函数,派生类专属成员部分尚未初始化,因此只会默认调用基类中的virtual函数,而不会下降至调用派生类中对应的函数。
    在构造和析构函数期间不要调用virtual函数。

    五、operator=返回一个(reference to *this)

    为了实现“连锁赋值”,赋值操作符必须返回一个“引用”指向操作符的左侧实参。

            Widget & operator = (const Widget &rhs)
            {
                ...
                return *this;
            }
    

    所有内置类型和标准程序库提供的类型如string,vector,complex或即将提供的类型共同遵守。
    令赋值操作符返回一个reference to *this。

    六、在operator= 里处理自我赋值##

    Widget& Widget::operator== (const Widget& rhs){
        if(this == &rhs) return *this
        
        ···
    }
    

    带有指针

     Widget& Widget::operator=(const Widget& rhs)
         {
             Bitmap *pOrig = pb;                //记住原先的pb
             pb = new Bitmap(*rhs.pb);      //令pb指向*pb的一个复本
             delete pOrig;                           //删除原先的pb
             return *this;  //这样既解决了自我赋值,又解决了异常安全问题。自我赋值,将pb所指对象换了个存储地址。
         } 
    

    operator=要注意自我赋值的情况

    七、复制对象时务忘其每一个成分##

    • 如果你在类中添加一个成员变量,你必须同时修改相应的copying函数(所有的构造函数,拷贝构造函数以及拷贝赋值操作符)。
    • 在派生类的构造函数,拷贝构造函数和拷贝赋值操作符中应当显示调用基类相对应的函数。
    • Copying函数应该确保复制“对象内的所有成员变量”及“所有基类成员”。
    • 不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数(如inite)中,并由两个copying函数共同调用。
  • 相关阅读:
    Java日志第8天 2020.7.13
    Java日志第7天 2020.7.12
    Java日志第6天 2020.7.11
    Java日志第5天 2020.7.10
    Java日志第4天 2020.7.9
    Java日志第3天 2020.7.8
    设计模式_23种设计模式_目录
    ICacheEntry中SlidingExpiration与AbsoluteExpirationRelativeToNow的区别
    MySql中的replace into
    结巴分词
  • 原文地址:https://www.cnblogs.com/narjaja/p/9323073.html
Copyright © 2011-2022 走看看