探究c++對象模型之寫在前面

第一章.關於對象


前言

c語言中數據和處理是分開聲明的,也就是說語言本身沒有支持數據和處理的關聯性,我們把這種程序方法稱爲程序性。

而有一組以功能爲嚮導的函數所驅動,共同處理共同的外部數據,這種稱爲對象性。


對象與成本

關於c++的對象,有人說由程序性到對象會增加成本,我們看看是否真的是這樣的:

先考慮對於一個擁有成員變量的類,這個並沒有增加成本。然後當類中增加了類成員函數,對於類成員函數,類成員函數雖然包含在類聲明中,但是不屬於對象。關乎類成員函數中有一個inline,當類成員函數被標記爲內聯,在每一個對象調用內聯函數都會產生一個函數實例,這麼做提高了效率,這個並不是成本的增加。

真正影響成本的是由virtual引起的:

Virtual function機制,用以支持一個有效率的“執行期綁定”。

Virtual base class用以實現“用以實現多次出現在繼承體系的base class,有一個單一而共享的實例”。


c++的對象模式

 

簡單對象模型:

 

目的:儘量減輕c++編譯器設計的複雜度而開發出來的,賠上的是空間和執行期的效率。

實現原理:由一張指針表組成,所有指針都指向對象成員。

應用:c++指向成員的指針。


表驅動對象模型:


目的:爲了使class所以的對象具有一致性。

實現原理:產生兩個指針,一個指向data number table和一個 number function table

應用:虛表。


c++對象模型:


每一個class產生出一堆指向virtual function的指針,放在表格裏,稱爲虛表。

每一個class object被安插一個指針,指向相關的虛表。虛表的設定交由構造函數,每個類關聯的 type_info object也存放在虛表之中,通常放在表格的第一個slot

 

//其實關於多繼承還是蠻複雜的

 

當多繼承出現時,佈局可能是這樣的:

每一個derived class object內的一個slot指出base class的地址。

優點是對象不會因爲base class的改變而影響。

缺點是由於間接性而導致的空間和存儲時間上的額外負擔。

 

構造函數的語義學

 


defaultconstructor的構造操作

 

c++標準說:當用戶未曾創建構造函數時,編譯器會提供給用戶一個trivialconstructor

 

那麼什麼時候會提供呢?

當編譯器需要一個constructor的時候。

 

一共有四種情況:

  1. 當一個類中包含class object number,並且這個class object number含有一個 default constructor
  2. 當一個類含有base class的時候,並且這個 base class 具有default costructor的時候。
  3. 當一個類具有virtual function的時候。
  4. 當一個類是以virtual繼承的方式繼承的時候。

 

Copy constructor的構造操作

其實關於default constructor我都知道做了些什麼,關於這個copy,我們會在哪裏用到它呢?

  1. 拷貝構造
  2. 函數的值傳遞和函數的返回值構造。

Copyconstructor的內部構造方式:

把每一個內建的data member的值拷貝一份給另一個object,如果data member也是一個類,則遞歸,這個叫memberwiseinitialzation。(成員逐次初始化?)

 

而實際上Arm告訴我們說,概念上這,對於一個classX這個操作是由copy constructor實現的。而實際上一個良好的編譯器可以爲大部分class object產生bitwrist copy ,因爲他們是具有bitwrist copy語義的。


那麼什麼叫bitwrist copy呢?位逐次拷貝。

 

我發現位逐次拷貝和我們所說的淺拷貝差不多,但是真的差不多麼?我們一會再看

 

我們先說一下什麼時候不會展現bitwristcopy,而由編譯器爲你創建一個copy construct

1.當一個類中含有類對象,而這個類對象中含有copy construct的時候。

2.base class中含有一個copyconstruct中的時候。

3.class中含有virtualfunction的時候。(因爲虛表的創建是由三大構造函數指明的。這裏沒算上移動構造函數)

4.class派生自一個繼承串,而其中有一個或多個virtual base class 的時候。(這種情況和其他三種不太一樣,因爲其他都發生平行賦值就會引發copy construct(也就是說類對象與類對象之間的),而第四種是發生在派生類與父類之間的賦值纔會體現,因爲virtual base class 是動態的,由於各個編譯器實現不同我們可能會改變offset或者其他如virtualbase class 的位置啊,什麼的?這完全取決於編譯器實現,但是,一定會引發copy construct的產生是毋庸置疑的)

 

淺拷貝bitwrist copy

淺拷貝與bitwrist copy並不是一個東西,淺拷貝主要是用來說明在類成員中存在指針的情況下,淺拷貝會造成的種種原因的一個表象說法,而關於bitwrist copy用來針對與copyconstructor的一個大範圍的一種copymeathod

 

關於bitwrist copymemberwristcopy的關係,我還有一點自己的理解就是memberwristcopy是依賴bitwrist copy的,對於基本類型我們只能通過bitwrist copy,因爲基本類型沒有copy constructor

 

對於memberwrist copybitwristcopy,我看到的第一個想法就是關於內存泄漏方面,事實上來說,有點關係,只不過這個是專門用來描述對象模型的,對於內存泄漏會有一定幫助但是幫助不大。


程序轉化語義學

 

這一章講了,編譯器爲我們做了什麼,使得我們在編寫程序的時候“得心應手”,與編譯器的優化。

 

 

顯示初始化操作:

 

我們在顯示初始初始化的時候是如何書寫的,如下:

 

已知:
X x0;
則
X x1(x0);
 
這段代碼被程序轉化成什麼樣呢?
 
1.x1被重新定義,剝奪其初始化操作:
X x1;
2.調用X的copy construct
x1.X::X(x0);


 

參數的初始化:

 

當我們在傳參的時候直接通過在參數內構造臨時對象時,也會分爲兩個步驟:

1.將構造方法與函數調用剝離,構造一個臨時對象。

2.將臨時對象傳入函數。

 

也就是說我們會依次調用兩個構造函數,所以會造成什麼後果你懂的。

 

但是有的編譯器會把實際參數直接構建在應該的位置上。也就說我們少構建一次。

 

返回值的初始化:

 

對於一個X bar();

 

優化方式如下:增加一個額外參數,類型爲class object的一個引用(X&,在返回指令前安插一個copy construct從而獲得返回值。

 

如下:

 

X bar();
 
 X vczh =bar();
 
 
僞代碼
Voidbar(X& _result){
 
X x;
 
 _result.X::X(x);
Return;
}
 
 
  //剝奪初始化的權利
Xvczh;         
bar(vczh);

  

在使用者層面做優化:

 

這個沒什麼說的了,所謂使用者優化,就是垃圾程序員寫的垃圾代碼最後由編譯器收拾爛攤子,收拾的好壞,能不能收拾,這個呵呵。

 

編譯器層面做優化:


這裏介紹了
Name Return Value (NRV)優化,我所理解的NRV優化就是省去代碼中最後作爲返回結果的臨時對象,而通過返回值的初始化,直接對返回值進行修改。從而減少一次對象構造的機會。

 

我在想爲什麼NRV優化對於編譯器這麼重要,我們也可以寫成像NRV優化出來一樣的代碼啊,我們也可以剝奪初始化的權利,引用調用,我覺得實際上,這個代碼對於審閱的人來說太複雜了不是嗎?他需要順着你的思維去了解代碼,要是豬隊友怎麼辦?他不懂,那麼糟糕了。我們在寫代碼的時候不要過分追求這些,有些東西底層幫你做了,那麼你就負責寫出能被編譯器儘量優化的和能被隊友審閱的代碼,那麼就ok了。

 

成員初始化列表


1.當初始化一個reference member時。

2.當初始化一個const member

3.當調用一個基類的構造函數的時候。

4.當調用一個member classconstructor,而他擁有一組參數的時候。

 

list中的項目順序是由class中的member聲明順序決定的,不是由,initializationlist中的排列順序覺定的。

List是先於函數體內構造的,所以爲了防止編譯器爲我們在構造函數體內添加東西,我們應該在list中構造而不是選擇在函數體內構造。

儘量不要在list使用類成員函數對類成員進行初始化,你不知道成員函數對類對象的依賴性有多高。

 




發佈了39 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章