Effect C++ 读书笔记: 构造函数/析构函数/赋值运算符

来源:互联网 发布:淘宝女装软文 编辑:程序博客网 时间:2024/05/15 10:26
* 本工程名CDAProjct表示 C++中的构造/析构和赋值 */

#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstdlib>

using namespace std;

/**************************************/
/*    条款1:c++默默编写并调用的函数       */
/**************************************/


/* default构造函数和析构和析构函数 */

class Another
{
public:
    Another() { cout<<"This is Another's constrctor!"<<endl; }
    ~Another() { cout<<"This is Another's desconstructor!"<<endl; }
};

class Base
{
    public:
    Base() { cout<<"base class constructed!"<<endl; }
    ~Base() { cout<<"base class desconstruct!"<<endl; }
    private:
        Base& operator=(const Base& rhs) { return *this;}
};

class Derived : Base
{
    public:
    Derived() { cout<<"derived class constructed!"<<endl; }
    ~Derived() { cout<<"derived class desconstructed!"<<endl; }
    private:
        Another an;
};

/* main中运行{ Derived deri; } 的结果是  */
/* derived class constructed! */
/* base class constructed! */
/* base class desconstruct! */
/* derived class desconstructed! */
/* 说明deri的构造过程是这样的:先执行派生类的构造函数,再执行基类的构造函数 */
/* 析构的过程是这样的:先执行积累的析构函数,再执行派生类的构造函数 */
/* Effective C++ :deafult构造函数和析构函数主要是给编译器一个地方用来设置"藏身幕后"的代码 */
/* 像是调用base classs 和 no-static成员变量的构造函数和析构函数 */

/* 在上面写一个Another类之后,在Derived类中添加一个Another的成员变量 */
/* 运行结果是: */
/* derived class constructed! */
/* This is Another's constrctor! */
/* base class constructed! */
/* base class desconstruct! */
/* This is Another's constrctor! */
/* derived class desconstructed! */
/* 了解到 Effective C++上的句话的意思了吧,并且在一个派生类中构造函数的执行过程是这样的: */
/* 先执行基类的构造函数,再执行成员变量的构造函数,最后执行自己的构造函数 */


/******************************************************************/
/* copy构造函数和copy assignment操作符 */
/* Effective C++:上面两者的编译器默认版本只是将源对象的每一个no-static成员变量拷贝到目标对象 */
template <typename T>
class NameObject
{
public:
    NameObject(const char* name,const T& value);
    NameObject(const std::string& name,const T& value);
private:
    std::string nameValue;
    T objectValue;
};

template <typename T>
NameObject<T>::NameObject(const char* name,const T& value):nameValue(name),objectValue(value)
{

}

template <typename T>
NameObject<T>::NameObject(const std::string& name,const T& value):nameValue(name),objectValue(value)
{

}

/* copy构造函数使用示例 */
/*
    NameObject<int> no1("This is me",2);
    NameObject<int> no2(no1);
*/

/* 注意编译器为类默认生成的copy assignment操作符的使用情况 */
/* Effective C++: 默认生成的copy assignment操作符的行为基本上*/
/* 与生成的copy构造函数如出一辙,但一般而言只有生成的代码合法且有适当的机会证明它 */
/* 有意义时,其表现才如copy构造函数,否则编译器拒绝为类生成默认的 copy assignment操作符*/
template <typename T>
class NameObject1
{
public:
    //NameObject1(const char* name,const T& value);
    NameObject1(std::string& name,const T& value);
private:
    std::string& nameValue;
    const T objectValue;
};

template <typename T>
NameObject1<T>::NameObject1(std::string& name,const T& value):nameValue(name),objectValue(value)
{

}

/* 实验结果 */
/*
    std::string newDog("helly");
    std::string oldDog("marker");
    NameObject1<int> p(newDog,2);
    NameObject1<int> s(oldDog,36);
    p=s;
*/
/*
结果如下:
错误: non-static reference member ‘std::string& NameObject1<int>::nameValue’, can’t use default assignment operator|
non-static const member ‘const int NameObject1<int>::objectValue’, can’t use default assignment operator|
在这里第一次需要生成的方法‘NameObject1<int>& NameObject1<int>::operator=(const NameObject1<int>&)’ |
*/

/*
    错误原因:C++不容许"让reference该指向不同的对象"
    Effective C++: 如果打算在一个"内含reference成员"的class内支持赋值操作符,你自己必须定义自己的copy assignment操作符
    面对"内含const的成员"的class也是一样的
*/

/* 最后一个关于copy assignment操作赋应该注意的地方是 */
/* 如果某个base class 将copy assignment操作赋声明为private */
/* 编译器将拒绝为其derived class生成copy assignment操作赋 */
/*
class Base
{
    public:
    Base() { cout<<"base class constructed!"<<endl; }
    ~Base() { cout<<"base class desconstruct!"<<endl; }
    private:
        Base& operator=(const Base& rhs) { return *this;}
};

class Derived : Base
{
    public:
    Derived() { cout<<"derived class constructed!"<<endl; }
    ~Derived() { cout<<"derived class desconstructed!"<<endl; }
    private:
        Another an;
};

int main(void)
{
    Derived dir1;
    Derived dir2;
    dir2=dir1;
    return 0;
}

结果:
错误: ‘Base& Base::operator=(const Base&)’是私有的|
错误: 在此上下文中|
附注: 在这里第一次需要生成的方法‘Derived& Derived::operator=(const Derived&)’ |
*/

/*********************************************************/
/*     条款2:若不想使用编译器自动生成的函数,就该明确拒绝         */
/*********************************************************/
/* 通常如果你不希望class支持某一特定机能,只要不声明特定的函数就行了  */
/* 但这个策略对copy构造函数和copy assignment操作符不适用,因为如果 */
/* 你自己不声明,编译器会为你自动产生 */

/* 解决方法:将copy构造函数和copy assignment操作符声明为private */
/* 明确声明一个成员函数,阻止编译器暗自创建其专属版本;将这些函数声明为 */
/* private,可以阻止别人调用它 */

/* 将成员函数声明为private而故意不去实现它,是我们的常用伎俩 */
class HomeForSale
{
public:
    HomeForSale() {}
    friend void useprivate(HomeForSale& homeleft,const HomeForSale& homeright)
    {
        homeleft=homeright;
    }
private:
    HomeForSale(const HomeForSale&);
    HomeForSale& operator=(const HomeForSale&);
};

/*
    HomeForSale home1;
    HomeForSale home2(home1);
    HomeForSale home3;
    home3=home1;
    结果:
    错误: ‘HomeForSale::HomeForSale(const HomeForSale&)’是私有的|
    错误: 在此上下文中|
    错误: ‘HomeForSale& HomeForSale::operator=(const HomeForSale&)’是私有的|
    错误: 在此上下文中|
*/

/*
    friend void useprivate(const HomeForSale& homeleft,const HomeForSale& homeright)
    {
        homeleft=homeright;
    }
    错误: 将‘const HomeForSale’作为‘HomeForSale& HomeForSale::operator=(const HomeForSale&)’的‘this’实参时丢弃了类型限定
    [-fpermissive]|
    friend void useprivate(HomeForSale& homeleft,const HomeForSale& homeright)
    {
        homeleft=homeright;
    }
    就可以运行了
*/

/* 一种使用继承的方法来阻止copying行为 */
/* 设计一个专门为了阻止copying动作而设计的base class */
class Uncopyable
{
    public:
    Uncopyable() {};
    ~Uncopyable() {};
    private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator= (const Uncopyable&);
};

/* 为了阻止某类的对象被拷贝,要做的就是继承这个类 */
class HomeForSale2 : private Uncopyable
{

};

/*
    HomeForSale2 home1;
    HomeForSale2 home2;
    home2=home1;
    结果:
    错误: ‘Uncopyable& Uncopyable::operator=(const Uncopyable&)’是私有的|
    错误: 在此上下文中|
    附注: 在这里第一次需要生成的方法‘HomeForSale2& HomeForSale2::operator=(const HomeForSale2&)’ |
*/

/***************************************************/
/*       条款3:为多态基类声明virtual析构函数           */
/***************************************************/

/* C++明确指出:当derived class对象经由一个base class指针被删除   */
/* 而该base class带着一个no-virtual 析构函数,其结果未有定义--实际 */
/* 执行时通常发生的是对象的derived部分没被销毁 */

class BaseClass
{
public:
    BaseClass() {}
    ~BaseClass() { cout<<"This is the base class destructor!"<<endl; }
private:
    };

class DerivedClass: public BaseClass
{
public:
    DerivedClass() {}
    ~DerivedClass() { cout<<"This is the derived class destructor!"<<endl; }
private:
};

/*
    BaseClass* pd= new DerivedClass();
    delete pd;
    结果:
    This is the base class destructor!
    表明只有baseclass部分被析构!
*/

/* 上述问题的解决方法:给base class一个virtual析构函数 */
class BaseClass1
{
public:
    BaseClass1() {}
    virtual ~BaseClass1() { cout<<"This is the base class destructor!"<<endl; }
private:
};

class DerivedClass1: public BaseClass1
{
public:
    DerivedClass1() {}
    ~DerivedClass1() { cout<<"This is the derived class destructor!"<<endl; }
private:
};

/*
    BaseClass1* pd= new DerivedClass1();
    delete pd;
    结果:
    This is the derived class destructor!
    This is the base class destructor!
    两个析构函数都被执行,且先执行的是派生类的析构函数
*/

/* virtual函数和virtual析构函数之间的关系 */
/*
    virtual函数的目的是容许derived class的实现得以客制化
    (即每个derived class可以根据自己的需要来自定义自己继承的virtual函数)
    任何class只要带有virtual函数都几乎确定应该也有一个virtual
    析构函数
*/
/* 当class不企图被当作base class,令其析构函数为virtual往往是一个馊主意 */
/* 虚表和虚表指针(virtual table and virtual table point) */
/* 最好不要继承没有virtual析构函数的类:比如说各种标准容器 */

/* 使用纯虚函数定义抽象类(pure virtual) */
class Pure
{
public:
    virtual ~Pure()=0;
};

Pure::~Pure()
{

}

/******************************************************/
/*               条款4:别让异常逃离析构函数               */
/******************************************************/

/* C++不喜欢析构函数吐出异常 */
/* 两个异常同时存在的情况下,程序若不是结束执行就是导致不明确的行为 */


/*
C++中的异常机制 :
异常机制提供了程序中错误检测和错误处理之间的通信,C++的异常处理包括:
1.throw表达式:错误检测部分使用这种表达式来说明遇到了不可处理的错误,也就是说,throw引发了异常条件
2.try块:错误处理部分使用它来处理异常.
try块以try关键字开始,并以一个或多个catch子句结束
3.由标准库所定义的一组异常类,用来在throw和相关的catch之间传递相关的错误信息
*/

/*
try块的通用语法格式是:
try
{
    program-statements
}
catch(exception-specifier)
{
    handler-statements
}
catch(exception-specifier)
{
    handler-statements
}
*/

/*
    try
    {
        int i;
        cin>>i;
        if(i>10)
        {
            throw runtime_error("This is the end of the world!");
        }
        else
        {
            throw runtime_error("This is not the end of the world!");
        }
    }
    catch(runtime_error err)
    {
        cout<<err.what()<<endl;
    }
    上面是一段抛出异常代码的实例,当用户输入的i>10的时候,输出异常This is the end of the world!
    当用户输入i<10的时候,输出异常This is not the end of the world!
*/

/* 析构函数吐出异常的实验 */
class Widget
{
public:
    Widget() {}
    ~Widget()
    {
        throw runtime_error("There happens an error!");
    }
};

/*
    在main函数中定义下面的代码:
    {
      std::vector<Widget> v(10);
    }
    结果:
    terminate called after throwing an instance of 'std::runtime_error'
        what(): There happens an error
    Aborted (core dumped)
    原因是析构容器的第一个元素期间有一个异常被抛出,其他9个元素还是
    应该被销毁,否者就会出现内存泄露.当销毁第二个元素时又会抛出异常,
    这时两个异常同时发生作用;C++无法同时处理两个异常,程序若不是结束执行
    就是导致不明确的行为。
*/

/* 若析构函数必须执行一个动作,且该动作会在失败时抛出异常,那该如何解决? */
class DBConnection
{
public:
    DBConnection() {}
    static DBConnection create();
    void close();
};

DBConnection DBConnection::create()
{
    return DBConnection();
}

void DBConnection::close()
{
    throw runtime_error("close error.");
}

class DBConn
{
public:
    DBConn() {}
    ~DBConn()
    {
        try
        {
            db.close();
        }
        catch(runtime_error err)
        {

        }

    }
private:
    DBConnection db;
};

/*
在main函数中调用先面的代码:
{
        DBConn dbc;
}
结果:
terminate called after throwing an instance of 'std::runtime_error'
        what(): close error.
Aborted (core dumped)
为和会出现这种情况呢?
因为DBConn的析构函数调用db.close()时会抛出异常
这是,程序会退出这个析构函数,导致内存的泄露
*/

/* 下面是避免上述问题的两个方法 */
/* 1.若close抛出异常就结束程序 */
/*
将~DBConn()调用db.close()的代码修改为下面的一段代码
~DBConn()
    {
        try
        {
            db.close();
        }
        catch(runtime_error err)
        {
            cout<<err.what()<<endl;
            abort();
        }

    }
运行结果:
close error.
Aborted (core dumped)
如果程序遭遇了一个"于析构期间发生的错误后"无法继续执行,
“强迫结束该程序”是一个合理的选项,这样可以阻止异常从析构函数中传播出去
*/

/* 吞下因调用析构函数而发生的异常 */
/*
~DBConn()
    {
        try
        {
            db.close();
        }
        catch(runtime_error err)
        {
            // 不做任何动作
        }

    }
*/

/* 上述两种做法都没有什么吸引力 */


/************************************************************/
/*       条款5:绝对不要在构造和析构过程中调用virtual函数           */
/************************************************************/
class Transaction
{
public:
    Transaction();
    virtual void logTransaction() const = 0;
};

void Transaction::logTransaction() const
{
    cout<<"Transaction: log"<<endl;
}

Transaction::Transaction()
{
    logTransaction();
}

class BuyTransaction: public Transaction
{
public:
    virtual void logTransaction() const;
};

void BuyTransaction::logTransaction() const
{
    cout<<"Buy: log"<<endl;
}

class SellTransaction: public Transaction
{
public:
    virtual void logTransaction() const;
};

void SellTransaction::logTransaction() const
{
    cout<<"sell : log"<<endl;
}

/*
在main编写下面的语句:
BuyTransaction b;
结果:
Transaction: log
原因:
derived class 对象内的base class成分会在derived class自身
成分构造之前先构造妥当。base class 构造期间virtual函数绝对不会
下降到derived classes阶层。
为什么?
:当base class构造函数执行时derived class的成员变量尚未初始化
"要求使用对象内部尚未初始化的部分"是危险的代名词,C++不容许你这样做。

*/

/* 在derived class中安全初始化Base class部分的方法:*/
/*
    
*/

int main()
{
    BuyTransaction b;
    return 0;
}
原创粉丝点击