淺談構造函數與析構函數的調用順序

http://blog.sina.com.cn/s/blog_4c4d6e740100iq69.html


構造函數

    先看看構造函數的調用順序規則,只要我們在平時編程的時候遵守這種約定,任何關於構造函數的調用問題都能解決;構造函數的調用順序總是如下:
1.基類構造函數。如果有多個基類,則構造函數的調用順序是某類在類派生表中出現的順序,而不是它們在成員初始化表中的順序。
2.成員類對象構造函數。如果有多個成員類對象則構造函數的調用順序是對象在類中被聲明的順序,而不是它們出現在成員初始化表中的順序。
3.派生類構造函數。

析構函數
    析構函數的調用順序與構造函數的調用順序正好相反,將上面3個點反過來用就可以了,首先調用派生類的析構函數;其次再調用成員類對象的析構函數;最後調用基類的析構函數。
    析構函數在下邊3種情況時被調用:
    1.對象生命週期結束,被銷燬時(一般類成員的指針變量與引用都i不自動調用析構函數);
    2.delete指向對象的指針時,或delete指向對象的基類類型指針,而其基類虛構函數是虛函數時;
    3.對象i是對象o的成員,o的析構函數被調用時,對象i的析構函數也被調用。

下面用例子來說說構造函數的的調用順序:
#include "stdafx.h"
#include "iostream"
using namespace std;
class Base
{
public:
    Base(){ std::cout<<"Base::Base()"<<std::endl; }
    ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};

class Base1:public Base
{
public:
    Base1(){ std::cout<<"Base1::Base1()"<<std::endl; }
    ~Base1(){ std::cout<<"Base1::~Base1()"<<std::endl; }
};

class Derive
{
public:
    Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
    ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};

class Derive1:public Base1
{
private:
    Derive m_derive;
public:
    Derive1(){ std::cout<<"Derive1::Derive1()"<<std::endl; }
    ~Derive1(){ std::cout<<"Derive1::~Derive1()"<<std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Derive1 derive;
    return 0;
}

運行結果是:
Base::Base()
Base1::Base1()
Derive::Derive()
Derive1::Derive1()
Derive1::~Derive1()
Derive::~Derive()
Base1::~Base1()
Base::~Base()

那麼根據上面的輸出結果,筆者稍微進行一下講解,構造函數的調用順序是;首先,如果存在基類,那麼先調用基類的構造函數,如果基類的構造函數中仍然存在基類,那麼程序會繼續進行向上查找,直到找到它最早的基類進行初始化;如上例中類Derive1,繼承於類Base與Base1;其次,如果所調用的類中定義的時候存在着對象被聲明,那麼在基類的構造函數調用完成以後,再調用對象的構造函數,如上例中在類Derive1中聲明的對象Derive m_derive;最後,將調用派生類的構造函數,如上例最後調用的是Derive1類的構造函數。

virtual析構函數
下面來說一說爲多態基類聲明virtual析構函數:
在C++中,構造函數不能聲時爲虛函數,這是因爲編譯器在構造對象時,必須知道確切類型,才能正確的生成對象,因此,不允許使用動態束定;其次,在構造函數執行之前,對象並不存在,無法使用指向此此對象的指針來調用構造函數,然而,析構函數是可以聲明爲虛函數;C++明白指出,當derived class對象經由一個base class指針被刪除,而該base class帶着一個non-virtual析構函數,其結果未有定義---實際執行時通常發生的是對象的derived成分沒被銷燬掉。

看下面的例子:
class Base
{
public:
    Base(){ std::cout<<"Base::Base()"<<std::endl; }
    ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};

class Derive:public Base
{
public:
    Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
    ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Derive(); 
    //這種base classed的設計目的是爲了用來"通過base class接口處理derived class對象"
    delete pBase;

    return 0;
}

輸出的結果是:
Base::Base()
Derive::Derive()
Base::~Base()
從上面的輸出結果可以看出,析構函數的調用結果是存在問題的,也就是說析構函數只作了局部銷燬工作,這可能形成資源泄漏 敗壞數據結構等問題;那麼解決此問題的方法很簡單,給base class一個virtual析構函數

class Base
{
public:
    Base(){ std::cout<<"Base::Base()"<<std::endl; }
    virtual ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};

class Derive:public Base
{
public:
    Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
    ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Derive();
    delete pBase;

    return 0;
}

輸出結果是:
Base::Base()
Derive::Derive()
Derive::~Derive()
Base::~Base()
可能上面的輸出結果正是我們所希望的吧,呵呵!由此還可以看出虛函數還是多態的基礎,在C++中沒有虛函數就無法實現多態特性;因爲不聲明爲虛函數就不能實現“動態聯編”,所以也就不能實現多態啦!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章