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

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

  (1)、默认构造函数c++

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

  (2)、析构函数ide

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

  (3)、拷贝构造函数函数

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

  (4)、赋值构造函数this

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

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

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

  

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

禁止被拷贝构造或赋值构造的作法:设计

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

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

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

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

  

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

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

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

  引伸:c++11中能够override,final关键字指定。只能做用于虚函数

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

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

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

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

 

0八、别让异常逃离析构函数

  (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;
};

 

0九、不要在构造和析构过程当中调用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

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

  用于链式赋值

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

  推荐作法:

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

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

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

十二、复制对象时勿忘其每个成分

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

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

相关文章
相关标签/搜索