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

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

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

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

2.編譯器什麼時候合成缺省構造函數?

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

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

B.直接或者間接的含有virtual function,好比本身含有virtual function或者是類成員變量,或者是繼承的父類含有。測試

C.出現虛繼承的現象。ui

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

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

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 635672377@qq.com ** 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
複製代碼
相關文章
相關標籤/搜索