C++幕後故事(二)--編譯器合成默認的構造函數

編譯器合成缺省(無參)的構造函數

1.先考慮一個問題爲什麼c++中有構造函數?

這個可以從語言設計的角度來看這個問題。體現一個實例化的對象生命週期的完整性,一個對象在初始化的時候,讓使用者有機會做些額外的初始化操作。同樣,一個對象是消亡的時候,也要使用者有機會去釋放資源。舉個例子:喫飯前先洗手(構造函數),喫完飯在擦嘴(析構函數),這是個好習慣。但是你不洗手,不擦嘴,也沒關係,只是這不是個好習慣而已(偶爾造成細菌感染,程序異常)。

2.編譯器何時合成缺省構造函數?

好習慣的養成是一個持續的過程,所以有時候編譯器會偷偷的幫你洗手擦嘴。

A.間接的含有構造函數,比如類成員變量含有構造函數,或者是繼承的父類有構造函數。

B.直接或者間接的含有virtual function,比如自己含有virtual function或者是類成員變量,或者是繼承的父類含有。

C.出現虛繼承的現象。

 

3.那麼構造函數的兄弟拷貝構造?

拷貝構造和缺省的構造也是同樣的A,B,C情況下,編譯器會合成默認的拷貝構造。當然編譯器生成的拷貝構造就是淺拷貝。

4.那麼析構函數呢?

編譯器不關心你有沒有寫析構函數,編譯器都會主動生成析構函數。在析構的時候,編譯會首先調用你寫的析構函數,然後在調用編譯器的析構函數,這個過程對於程序員來說是透明的。從反彙編的角度看,編譯器會生成一個析構函數,但是這個析構函數沒有實質作用,也沒有生成彙編代碼

// 沒有析構函數的類
class NoDctorClass
{
public:
    int m_a;
};

// 測試編譯器合成析構函數
void test_compiler_geneator_def_dctor()
{
    NoDctorClass no_dctor_class;
    // 調用析構函數
    no_dctor_class.~NoDctorClass();
    no_dctor_class.m_a = 0;
}
// 轉到反彙編的代碼
//    NoDctorClass no_dctor_class;
//    調用析構函數,這裏沒有生成任何的反彙編代碼
//    no_dctor_class.~NoDctorClass();
//    no_dctor_class.m_a = 0;
012AA9FE  mov         dword ptr [no_dctor_class],0  

5.那麼編譯器爲什麼幫你合成(拷貝)構造函數?

簡單一句話,編譯器需要插入一些額外的初始化代碼,來完成一些語言特性。

A.間接的含有缺省構造函數,這時候編譯器發現你自己沒有寫構造函數,但是你又間接含有構造函數。編譯器這時候有兩種做法,一種是心想算了吧我幫你偷偷的生成一個吧,還有一種做法就是編譯器報語法錯,讓程序員自己解決。所以編譯器廠商一商量覺得還是對程序員友好一點,用第一種做法吧。反過來想,如果一門開發語言對程序員不友好,可以說是它的生存期將會非常短。

B.直接或者間接的含有virtual function,因爲含有virtual function,爲了支持多態的特性,那麼每個實例對象都會生成指向虛函數表的指針。但是這個指針在什麼時候初始化呢?自然是(拷貝)構造函數裏面。但是你自己沒有寫,編譯器只好累一點生成(拷貝)構造函數。

C.虛繼承情況,爲了避免子類中含有多餘的成員變量,對象在實例化的時候會生成指向虛基類表的指針。自然就會聯想到在(拷貝)構造函數的時候生成,同樣你自己沒有寫,編譯器管家來替你寫。

6.如何驗證編譯器合成了(拷貝)構造函數?

1.在你的菜單欄找到如下控制檯

2.cd到編譯生成目錄下,找到你生成的obj文件,比如我生成main.obj。

3.執行 dumpbin.exe /all main.obj >> main.txt,將文件格式翻譯爲COFF。

 4.查看main.txt 找到,如果所示找到1所對應的函數,下面的2就是編譯器準備插入的代碼,可以看到插入了MatrixA的構造函數。同理,可以驗證其他的情況。

 

7.知道編譯器在何時合成構造函數那麼又怎麼樣?

1.知道編譯器揹着我們做了那些小動作,讓我們可以更加了解這門語言背後實現的細節,做到胸中自有丘壑。

2.儘量不要依賴編譯器的操作。

3.要站在編譯器的角度去看問題。

8.總結:

構造函數和拷貝構造是你的左膀右臂,建議還是得要好好的利用。儘管有時候你不承認,但是編譯器這個管家還是會偷偷摸摸的給你裝個左膀右臂。如果說構造函數是你的左膀右臂,那麼析構函數就是你的一把強有力的武器用於保護自己,在對象生命結束之後能夠確保資源的正常釋放。

9.代碼示例:

/****************************************************************************
**
** Copyright (C) 2019 [email protected]
** All rights reserved.
**
****************************************************************************/

/*
    測試編譯器在何種情況會合成默認的構造函數
    同理可證:在何種情況下編譯器會合成默認的拷貝構造函數
*/

#ifndef default_constrcuctor_h
#define default_constrcuctor_h

#include <iostream>

using std::cout;
using std::endl;

namespace defualt_constructor
{

// 含有缺省的構造函數成員類對象
// #define has_default_ctor_member 1

// 繼承含有缺省的構造函數
// #define has_inherit_ctor_base 1

// 含有虛函數成員函數
// #define has_virtual_function 1

// 函數virtual函數的成員類對象
// #define has_virtual_func_member 1

// 父類含有虛函數
// #define has_inherit_virtual_func_base 1

// MatrixC, MatrixB 虛繼承MatrixD
// #define has_virtual_inherit_base 1

// 類成員變量含有copy ctor
// #define has_default_copy_ctor_member 1

// 父類含有拷貝構造函數
#define has_inherit_copy_ctor_base 1

// 含有虛函數成員函數
// #define has_virtual_function_copy_ctor 1

// 類對象成員含有虛函數
// #define has_virtual_function_copy_ctor_member 1

// 父類函數虛函數
// #define has_inherit_virtual_function_copy_ctor_base 1

// 虛繼承copy ctor
// #define has_virtual_inherit_function_copy_ctor_base 1

class MatrixD
{
};

#ifdef  has_virtual_inherit_base
class MatrixC : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixC : virtual public MatrixD
#else
class MatrixC
#endif // has_virtual_inherit_base
{
public: 
#ifdef has_inherit_ctor_base
    MatrixC() { cout << "MatrixC" << endl; }
#elif has_inherit_copy_ctor_base
    MatrixC() {}
    MatrixC(const MatrixC &rhs) { cout << "MatrixC copy ctor" << endl; }
#elif has_inherit_virtual_function_copy_ctor_base
    virtual void VirFun() {}
#elif has_inherit_virtual_func_base
    virtual void VirFun() {}
#endif // has_virtual_inherit_base
};

#ifdef has_virtual_inherit_base
class MatrixB : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixB : virtual public MatrixD
#else
class MatrixB
#endif // has_virtual_inherit_base
{
public:
#ifdef has_default_ctor_member
    MatrixB() { cout << "MatrixB" << endl; }
#elif has_virtual_func_member
    virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#elif has_default_copy_ctor_member
    MatrixB() {}
    MatrixB(const MatrixB &rhs) { cout << "copy ctor MatrixB" << endl; }
#elif has_virtual_function_copy_ctor_member
    virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#endif // has_default_ctor_member

    int m_high;
    int m_width;
};

#ifdef has_default_ctor_member
class MatrixA
#elif has_virtual_function
class MatrixA
#elif has_inherit_ctor_base
class MatrixA : public MatrixC
#elif has_inherit_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_func_member
class MatrixA
#elif has_virtual_inherit_base
class MatrixA : public MatrixB, public MatrixC
#elif has_inherit_virtual_func_base 
class MatrixA: public MatrixC
#elif has_inherit_virtual_function_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixA : public MatrixB, public MatrixC
#else
class MatrixA
#endif // has_default_ctor_member
{
public:
    int m_age;
    int m_score;

#ifdef has_default_ctor_member
    MatrixB matrixB;
#elif has_virtual_function
    virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_func_member
    MatrixB matrixB;
#elif has_default_copy_ctor_member
    MatrixA() {}
    MatrixB matrixB;
#elif has_inherit_copy_ctor_base
    MatrixA() {}
#elif has_virtual_function_copy_ctor
    virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_function_copy_ctor_member
    MatrixB matrixB;
#endif // has_default_ctor_member

};

void test_compiler_generator_def_ctor()
{
    //用dumpbin把.obj文件內容導出成可查看文件my.txt,
    // 這個my.txt格式,一般被認爲是COFF:通用對象文件格式(Common Object File Format);

    MatrixA matrix;
    matrix.m_age = 0;

    // 編譯器會在哪些情況下合成默認的構造函數?
    // 1.包含一個類成員變量,此成員變量含有默認的缺省構造函數。此時編譯器就會
    // 生成默認的構造函數。在這個合成的構造函數中插入代碼調用成員變量的構造函數
    // 2.繼承的父類含有缺省的構造函數,此時編譯器也會構造默認的構造函數,在子類合成的構造
    // 函數中插入代碼,調用父類的缺省構造
    // 3.包含虛函數。不管是子類,父類,還是包含的成員類對象(不包含任何構造函數),只要
    // 包含了virtual function,編譯器都會合成默認的構造函數
    // 4.含有虛繼承現象,grandfather, parent(虛繼承grand),child再繼承,
    // 爲了在構造函數中生成vbtable,虛基類表

    // 同樣的道理,copy constrcutor也和constructor也是在同樣的情況下,編譯器會合成默認
    // 的構造函數

    // A.含有默認的構造函數
    //  1.父類有默認的構造函數
    //  2.包含類對象的成員變量含有默認構造函數

    // B.虛函數
    //  1.不管是自己包含虛函數,還是類成員變量有虛函數,還是父類中虛函數

    // C.虛繼承
    //  1.編譯器爲了在插入vbtable 虛基類表
}

void test_compiler_generator_def_copy_ctor()
{
    MatrixA ma;

    MatrixA mb = ma;
    // 編譯器合成默認copy ctor時機和ctor是一樣的
}

}

#endif // default_constrctor_h

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章