建構函式語意學

2021-06-20 19:37:45 字數 4774 閱讀 9470

default建構函式操作:

首先需要說明:

帶來的第乙個問題,編譯器什麼時候合成預設建構函式(nontrivial)?答案是編譯器需要的時候,而不是程式需要的時候。

1)class foo ;

class bar ;

void foo_bar()

此時編譯器就會合成default 建構函式;類似

inline bar::bar()

編譯器合成的預設建構函式只滿足自己需要,不滿足程式需要。

我們需要定義建構函式滿足程式需要:類似

bar::bar()

編譯器的行動:如果class a內含乙個或者乙個以上的member class objects,那麼每個constructor必須呼叫每乙個member class的預設建構函式。(在已有的constructors之前安插一些**)

// c++偽**

bar::bar()

2)同樣的面對「帶有default constructor」的base class的 時候也會採用類似上面的行為來對待建構函式。

3)「帶有乙個virtual function」的class

對於virtual function編譯期間編譯器會做兩個事情:

1. 產生virtual function table,其中放入class的virtual function的入口位址。

2. 每乙個class object,會被編譯器合成乙個額外的vptr指向這個virtual table。

如果程式沒有提供建構函式,編譯器需要合成乙個建構函式來新增**做上面的兩件事情。如果程式提供了乙個以上的建構函式,編譯器就會在建構函式user code之前插入一些**來完成需要做的事情

4)帶virtual base class的class

虛基類的引入我們都知道主要在於解決多重繼承時,基類可能被多次繼承,虛基類提供乙個基類給派生類

class x ;

class a : pulic virtual x {};

class b : pulic virtual x {};

class c : pulic a, public b {};

void foo(const a* pa)

編譯器必須改變執行訪問操作的這些**,是x::i可以到執行期才決定下來。編譯器可能類似策略:

void foo(const a* pa)

然而_vbcx這類東西需要在object構造期間產生,所以對於class定義的每個constructor,編譯器會安插一些**來做「允許每個virtual base class 的執行期存期操作」**,如果沒有宣告任何constuctors編譯器必須合成乙個default constructor。

以上是對於編譯器合成default constructor的四種情況,容易的錯誤理解:

1)任何class如果沒有定義default construtor就會合成乙個

2) 合成的default constructor會顯示的設定"class內每乙個data member的預設值"

預設拷貝建構函式(default copy constructor

)、預設賦值運算子(

operator =

)和預設析構函式,是

c++類中的六大特殊成員函式中的三個。三者同時遵循乙個原則:「一榮俱榮、一損俱損」。如果三者其中的任意乙個被顯示定義了(defined

)那麼三者必須都被顯式定義。當果三者之一被程式設計師呼叫但未沒有被顯式宣告時,編譯器會隱含的實現這三個特殊成員函式。當用乙個類物件去初始化另乙個類物件時,需要用到拷貝建構函式;當用乙個類物件去設定另乙個類物件時,需要用到賦值運算子。

拷貝建構函式與賦值運算子都遵循「default memberwise assignment&initialization」原則,即對類中的每乙個資料成員進行依次複製,但是通常編譯器只採用

bitwise copy

方式複製(這樣能夠提高效率)。例如,對於只含有

pod成員資料的簡單類,

bitwise copy

方式綽綽有餘。但是以下幾種情況比較特殊:

1) 當class

內含有乙個

member object

時,並且後者的

class

中宣告了乙個

copy constructor時;

依照「default memberwise assignment&initialization」原則,初始化

member object

時,需要編譯器呼叫

member class

的拷貝建構函式,如果類中沒有顯式定義拷貝建構函式,就需要編譯器構造,來呼叫成員類的拷貝建構函式。

2) 當類的基類中至少有乙個含有拷貝建構函式時;

同樣依照「default memberwise assignment&initialization」原則,需要依次構造所有的基類成員,如果沒有顯式定義預設拷貝建構函式,那麼這部分工作就有編譯器來完成。

3) 當類中宣告乙個或多個virtual functions時;

4) 當類的派生鏈中有乙個或多個virtual base class時;

這裡由於虛函式的機制,需要初始化vbtl

和vptr

。這部分需要編譯器來完成(本身虛擬機制就是從編譯器角度來實現的)。

以上幾種情況如果程式設計師未顯式定義拷貝建構函式,編譯器會自動完成拷貝建構函式的實現,用以滿足編譯器的需要,不過此時編譯器合成的版本不在以bitwise的拷貝方式來完成。

以上四種情況都能容易理解,因為其進行bitwise copy的時候會發生錯誤,所以不展現bitwise copy semantics(需要特別注意的是1,2點中,需要base class和類成員變數所在class具有copy constructor)

關於第三種情況:

當編譯器匯入乙個vptr到class之中的時候,class就不在展現bitwise semantics,編譯器需要合成乙個copy constructor將vptr適當的初始化。

例子:

class zooani

class bear: public zooani

bear y;

bear w = y;

此時w的vptr被設定為指向bear class的virtual table。因此也就是所將y的vptr拷貝給w是vptr安全的.

zooani z = y;

此時z的vptr不能直接指向bear的virtual table(也就是不能直接通過將y的vptr"bitwise copy"到z的vptr。

上述例子表達的是合成的copy constructor會顯示設定object的vptr指向zooani的virtual table,而不是直接通過拷貝。

第四種情況:

virtual base class表示的必須讓「derived class object中virtual base class subobject位置」在執行期準備妥當,然而bitwise copy可能破壞這個位置

class zooani {}

class raccoon: virtual public zooani {}

class redpanda: public raccoon {}

redpanda red;

raccoon rac = red;  // 此時如果直接進行bitwise copy,virtual base class subobject位置發生破壞。

另外需要注意的是,合成copy constructor中,如int,指標等nonclass members也會被複雜。

例子:

class word

合成的copy constructor

inline word ::word( const word& wd)

從編譯器編譯連線角度,以上四種情況下如果未定義拷貝建構函式,編譯器為了編譯工作的順利進行,會自定義拷貝建構函式;從程式設計者角度,如果模擬較複雜(例如含有指標、引用等),單單依靠編譯器定義預設拷貝建構函式,程式是無法達到預定效果的,所以此時往往需要程式設計師顯式定義出自己的拷貝建構函式。

程式轉化語意學:

1)返回值的初始化:

x bar()

問題:返回值如何從區域性物件xx中拷貝?

編譯器的轉化

void bar(x& __result)

x xx = bar();編譯器轉化為

x xx;

bar(xx);

2) 編譯器層面named return value(nrv)優化:(有興趣可以在g++通過優化層次上開啟nrv和不開啟做下對比)

void bar(x& __result)

3)使用著層面的優化

x bar()

寫成  // 這種寫法更多的是考慮了效率和不是優先抽象化

x bar()

編譯器的轉化:

void bar(x& __result)

深淺拷貝基礎知識:memberwise copy(深拷貝) 和 bitwise copy(淺拷貝)

memberwise copy:

在初始化乙個物件期間,基類的建構函式被呼叫,成員變數被呼叫,如果它們有建構函式的時候,它們的建構函式被呼叫,這是乙個遞迴的過程.

bitwise copy:原記憶體拷貝,

例子,給定乙個物件object,它的型別是class base.物件object占用10位元組的記憶體,位址從0x0到0x9.如果還有乙個物件objecttwo,型別也是class base.那麼執行objecttwo( object);如果使用bitwise拷貝語義,那麼將會拷貝從0x0到0x9的資料到objecttwo的記憶體位址,.也就是說bitwise是位元組到位元組的拷貝。

C 建構函式語意學 預設建構函式

在 class 中,若程式設計師沒有為該 class object 定義 default constructors,則編譯器會根據需要產生乙個 implicit default constructor,該 implicit default constructor 被認為是 trivial 無用的 那...

深度探索C 物件模型 之 建構函式語意學

explicit被引入c 是為了使程式設計師能夠制止 單一引數的constructor函式 被當做乙個conversion 運算子。有四種情況 1 帶有default constructor 的member classobject 2 帶有default constructor 的base clas...

C 物件模型學習筆記2 建構函式語意學

jerry schwarz,iostream函式庫建構師,曾為了讓cin能夠求得乙個真假值,定義了conversion運算子operator int 這樣使用者寫出 if cin 語句,就會很方便。但當使用者想要寫cout intval時,不小心寫成了cin intval 結果編譯器沒有報錯,哈哈,...