Effective C++ 笔记(2)构造/析构/赋值运算

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

  (1)、默认构造函数

Empty(){...} //调用父类构造函数,non-static成员变量构造函数,不会默认初始化内置类型

  (2)、析构函数

~Empty(){...} //调用父类析构函数,non-static成员变量析构函数

  (3)、拷贝构造函数

Empty(const Empty& rhs){...} //单纯地将来源对象的每一个non-static变量拷贝到目标对象

  (4)、赋值构造函数

Empty& operator=(const Empty& rhs){...} //单纯地将来源对象的每一个non-static变量拷贝到目标对象

  类内有引用、const、及父类的拷贝构造函数,赋值构造函数为private时,编译器会拒绝生成这一类函数。换言之,如果类内有引用、const成员变量,或者其父类的相关函数不可访问时,必须手动生成。有指针类型变量时,存在“深拷贝、浅拷贝”问题!!!

class Empty:
{
public:
    Empty(){...}
    Empty(const Empty& rhs){...}
    ~Empty(){...}
    
    Empty& operator=(const Empty& rhs){...}
private:
    const int m_cInt; //只读成员变量
    int &ref; //引用型成员变量,必须在每个构造函数手动初始化
};

  

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

禁止被拷贝构造或赋值构造的做法:

  (1)、将相应成员函数声明为private且不去实现它。(链接期出错

class HomeForSale
{
public:
    HomeForSale();
    ~HomeForSale();
private:
    HomeForSale(const HomeForSale& rhs);    //只有声明
    HomeForSale& operator=(const HomeForSale& rhs);    
};

  (2)、private继承Uncopyable类(编译期出错)

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

  

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

  (1)、polymorphic(带多态性质的)base classes应该声明一个virtual析构函数,这样通过delete 基类指针时,也会调用其指向对象的析构函数。避免内存泄漏。如果一个class带有一个或多个virtual函数,它就应该拥有一个virtual析构函数。

  (2)、classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不应该声明virtual函数。(会多出一个vptr指针,占用内存),string及STL容器均为无virtual函数!尽量别继承它们做事。

  引申:c++11中可以override,final关键字指定。只能作用于虚函数

  override,表示此虚函数必定“重写”了基类中的对应虚函数。  

  final,(1)作用在虚函数:表示此虚函数已处在“最终”状态,后代类必定不能重写这个虚函数。  

       (2)作用在类:表示此类必定不能被继承 

  编译器将帮你检查是否“必定” 

 

08、别让异常逃离析构函数

  (1)、析构函数绝对不要抛出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后处理它们,或者结束程序;

  (2)、如果接口使用者需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作;

 

class DBConn
{
public:
    void close()
    {
        db.close()//可能抛出异常函数
        closed = true;
    }
    ~DBConn()
    {
        if (!closed) //如果客户没有主动关闭的话
        {
            try
            {
                db.close();
            }
            catch(...)
            {
                //制作运转记录,记下对close的调用失败
            }
            /* code */
        }
    }
private:
    DBConnection db;
    bool closed;
};

 

09、不要在构造和析构过程中调用virtual函数

class Transaction //基类
{
public:
    Transaction();
    ~Transaction();
    virtual void logTransaction const = 0;
};
Transaction::Transaction()
{
    ...
    logTransaction();//记录这笔交易
}

class BuyTransaction : public Transaction
{
public:
    BuyTransaction();
    ~BuyTransaction();
    virtual void logTransaction()const override; //记录这笔交易
};

  BuyTransaction b;时先执行Transaction::Transaction,此时传入的this指针为Transaction* 故调用的是Transaction::logTransaction(),此时BuyTransaction还没被构造出来。

  构造函数:base::base-->derive::derive

  析构函数:base::~base-->derive::~derive

10、令operator=返回一个reference to *this

  用于链式赋值

11、在operator= 中处理“自我赋值”

  推荐做法:

Widget& Widget::operator=(Widget rhs)
{
    swap(rhs); //成员函数,进行交换
    return *this;
}

  (1)、确保当对象进行自我赋值时operator=有良好的行为。包括考虑“来源对象”和“目标对象”的地址(是否为同一个)、精心周到的语句顺序、以及复制交换;

  (2)、当一个函数操作多个对象时,确保即使这些对象为同一个对象,其行为仍然正确。

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

  (1)、Copying函数应该确保复制“对象内的所有成员变量”及“所有base class”成分;

  (2)、不要用某个copying函数实现另一个copying函数。应该将共同的代码放进第三个函数中,并由两个copying函数共同调用。

 

posted @ 2018-06-21 14:56  -南  阅读(148)  评论(0编辑  收藏  举报