C++基本功和 Design Pattern系列 ctor & dtor

來源:互聯網
上載者:User
最近實在是太忙了,無工夫寫呀。只能慢慢來了。呵呵,今天Aear講的是class.ctor 也就是constructor, 和  class.dtor, destructor. 相信大家都知道constructor 和 destructor是做什麼用的,準系統我就不廢話了。下面先說效率的問題,讓我們看個簡單的例子:

class SomeClass;   // forward declaration

class AnotherClass {
private:
    SomeClass SomeClassInstance;
public:
    AnotherClass(const SomeClass & Para) { SomeClassInstance = Para; };
    ~AnotherClass();
};

也許這是很多初學者經常寫出來的代碼,Aear以前也寫過。讓我們來看看這段代碼有什麼問題。

首先需要說明的是,在一個class執行個體化之前,所有的member都會被初始化,如果member是個class,那麼那個class的 constructor就會被調用。也就是說,在運行AnotherClass的constructor之前,SomeClass的 constructor就已經運行了。接下來的代碼裡,SomeClassInstance又被重新執行了次 = 操作。也就是說,我們在給 SomeClassInstance附初值的時候,調用了2次SomeClass的method. 這個浪費也太大了,比較標準的方式是使用初始化列表,如下:

    AnotherClass (const SomeClass & Para): SomeClassInstance(Para) {};
如果有多個類成員,可以用","來分割,如:

    AnotherClass (const SomeClass & Para1, UINT32 Para2):
                   SomeClassInstance(Para1),
                   SecondAttr(Para2),
                   ThirdAttr(Para3) {};
值得注意的是, 類成員的初始化順序和在類中的聲明順序應該一致。這個是有compiler來控制的,並不根據你在AnotherClass的 constructor中提供的初始化順序來進行。所以,如果你想先初始化ThirdAttr,然後把ThirdAttr傳到SecondAttr作為初始化參數,是會失敗的。只有改變聲明順序才會成功。

同理,在聲明類變數被附初值的時候,使用拷貝建構函式,效率更高:

=====錯誤=====
class x1;
x1 = x2;

=====正確=====
class x1(x2);

===================分割線===================

從上面的例子可以看到,幾乎所有的class,都需要提供拷貝建構函式,也就是 className(const className &)。同時值得注意的是,如果提供了拷貝建構函式,一般也就需要提供 "="操作,也就是 className & operator =  (const className &),說到 operator =, 也有必要強調下implicit type conversion的問題,這將會在以後的章節張有詳細描述。至於為什麼要提供 operator =,舉個簡單的例子:
 
class1 {
public:
    class1() { p = new int[100]; };
    ~class1() { delete[] p; };
private:
    char* p;
} x1, x2;

如果class1不提供operator =, 那麼運行 x1 = x2的時候,C++會運行最基本的拷貝操作,也就是 x1.p = x2.p,那麼在 x1被釋放的時候,delete p;被執行。這時候 x2再要訪問p,p已經變成非法指標了。 也許有人會說,我才不會用x1 = x2這麼危險的操作,那讓我們看看更加隱性的操作吧,例子如下:

void func(class1 Para) {...};

func(x1);

這時候,c++會調用class1的拷貝建構函式,來把參數從x1裡拷貝到Para,如果class1沒有提供copy constructor,那麼c+ +就執行簡單拷貝工作,也就是 Para.p = x1。當func返回的時候,Para被釋放,調用 Para.~class1(),並且 delete p;那麼x1.p就變成非法指標了。

這樣大家就知道為什麼要同時提供copy constructor和 operator =了吧。特別是在class裡有指標的情況下,必須提供以上2個method。如果不想提供,可以把他們設為private,代碼如下:

class1 {
...
private:
    class1 (const class1 &);
    class1 & operator = (const class1 &);
}
這樣別人在執行 = 和 func()的時候就會報錯了。

還有,在聲明建構函式的時候,單參數的建構函式,最好都用explicit來聲明,例如:

class1 {
public:
    class1(int Para) {...}
    ...
};

其中class1(int Para)是個單參數的建構函式,如果執行下列操作,如:

class1 x1 = 2;

的時候,因為2不是class1,所以c++會用隱性的類型轉換,也就是把2轉換成class1,因此會調用class1(2),然後用operator  = 符值給 x1. 這種操作經常會產生很多問題。比如如果我們提供了 operator == ,那麼 在 if(x1 == 2)的時候,c++也會進行類似的操作,可能會產生我們不需要的結果。所以,對於這種單參數的constructor 最好做如下聲明:

explicit class1 (int Para) {...}

這樣做再執行 class1 x1 = 2;的時候就會報錯了,explicit的意思就是C++ 的compiler不能做隱性類型轉換,必須由程式員做type cast,比如:

class1 x1 = static_cast<class1>(2) 才會成功。

===================分割線===================
在運行constructor的時候,值得注意的一點就是,如果在constructor裡,要初始化會throw exception的代碼,一定要在constructor裡catch。比如:

class1 {
    class1()
    {
       pInt = new int[100];
       try {
           pClass2 = new pClass2;
       }catch(...)
       { delete pInt; throw; };
     }
}

大家看的明白了吧,如果不catch pClass2的exception,pInt分配的記憶體就不會釋放,因為constructor如果失敗,c++是不會調用destructor的。

===================分割線===================
最後關於destructor,需要注意的是,如果是被繼承的base class,destructor一定要是virtual。比如:

BaseClass ()
{
public:
    BaseClass();
    virtual ~BaseClass();
}

DerivedClass : public BaseClass()
{
public:
    DerivedClass();
    ~DerivedClass();
}

BaseClass * pBase = static_cast<BaseClass *>(new DerivedClass());
delete pBase;

如果BaseClass的destructor是virtual,那麼正確的ctor dtor調用順序是:

BaseClass();
DerivedClass();
~DerivedClass();
~BaseClass();

如果不是Virtual,調用順序是:

BaseClass();
DerivedClass();
~BaseClass();

也就是說,DerivedClass的衍生類別不能被正確調用,這主要是因為在delete的時候c++並不知道你delete的是  DerivedClass, 因此需要把BaseClass的 dtor 設定成 virtual, 這樣可以使用 vptr在 vtbl中尋找  destructor,從而能夠正確的調用destructor。

===================分割線===================
從上面的例子大家也看出來了,如果是衍生類別,那麼就要調用基類的constructor,在多層次的衍生類別建立過程中,所以基類的constructor都要被調用。 destructor同理。因此要想提高效率,可以在關鍵代碼短使用非衍生類別。

也許有人會說,所有的constructor和destructor都被compiler inline了,但是即使是inline並且 base class的constructor中不進行任何操作,c++也要為每個類設定vptr,也是有不需要的overhead。當然,我們得到效率的同時,失去的是可擴充性,良好的程式階層等等,大家要根據具體情況來權衡。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.