對於抽象類中的純虛函式並不是嚴格意義上無法呼叫,在繼承體系中可以呼叫,但是這種呼叫並不體現多型屬性,只能被靜態鏈結。如果將析構函式定義為純虛函式將面臨很多無必要的麻煩,因此一般不建議將析構函式定義為純虛函式。另外,對於虛函式是否應該宣告為const
,一般不建議,因為類的設計者不能假設將來需要繼承自當前類的行為。
在c++中宣告只包含簡單資料型別的類似c結構的類,將會被打上plain oi' data
的標籤,該資料在進行構建,拷貝,賦值,析構等操作時並不會按照觀念上那樣呼叫預設建構函式,拷貝建構函式,賦值函式,析構函式等。假設有程式:
typedef
struct
point;
point global;
point func()
如果有上面的程式,point
被貼上plain oi' data
的標籤,那麼對於物件的構造,拷貝,析構則有:
global
是全域性變數,該類的預設建構函式,預設拷貝建構函式和析構函式要麼沒被呼叫要麼就根本沒有生成,但是全部變數儲存在bss段,會被預設初始化為0;
區域性變數local
和全域性變數的情況類似,但是並不會被初始化,因此可能導致錯誤;
指標heap
的構造只是簡單的轉換為heap=__new(sizeof(point))
,建構函式同樣未定義或者呼叫;
指標指向的物件拷貝local
,此處並未呼叫拷貝建構函式,而是類似c那樣對記憶體進行逐位拷貝;
delete heap
並未呼叫析構函式,只是轉換為__delete(heap)
;
返回local
同樣只是簡單的位拷貝。
如果將上面的類修改為抽象資料型別:
class
point
private
:float _x,_y,_z;
}
對於抽象資料型別,上面的函式的類構造方式如下:
global
是全域性變數,其初始化方式會延遲到程式啟用,其構造會由定義好的建構函式初始化;
local
會被附加default point constructor
的inline expansion
,轉換為point local; local._x = 0.0; local._y = 0.0; local._z = 0.0
;
指標heap
會轉換為point *heap = __new(sizeof(point)); if(heap !=0) ;
然後是建構函式的inline expansion
;
*heap=local
使用簡單的位拷貝;
之後同上。
如果給類中新增乙個虛函式:
class
point
virtual
floatz(
);private
:float _x,_y,_z;
}
因為虛函式的存在必須為類物件維護虛函式表,虛函式表的初始化,銷毀等工作應該在構造和析構函式中完成。經過編譯器擴張後的建構函式和拷貝建構函式可能如下所示:
//建構函式
point* point::
point
(point *
this
,float x,
float y):_x
(x),
_y(y)
//拷貝建構函式
inline point* point::
point
(point *
this
,const point &rhs)
對於上面函式func
的呼叫,除了*heap=local
可能觸發賦值函式的合成與inline expansion
,其他都相同。
繼承體系中設計大量的細節,對於繼承體系的乙個類,編譯器可能對其建構函式做的擴充套件如下:
初始化列表會按照變數宣告順序放入建構函式;
如果有乙個物件並未出現在初始化列表中並且它有乙個預設建構函式,則會呼叫它的預設建構函式;
如果類有虛函式表指標,則該指標需要被指定;
當前類所有基類的建構函式會按照宣告順序呼叫:
虛基類的建構函式需要按照從左向右,由深到淺呼叫:
存在虛擬繼承時,必須保證虛基類子物件的初始化必須有最底層的派生類完成,否則可能會出現多次初始化。
如果在建構函式中呼叫虛函式,那麼虛函式執行的是被子類重寫的實體還是基類的實體?因為在建構函式中子類的虛函式表未能完全構建呼叫虛函式無法執行子類的重寫實體。一般情況下,vptr的構造時間能夠保證在基類的構造函式呼叫之後,使用者**或者初始化列表中所列成員初始化之前。一把情況下建構函式執行演算法如下:
在繼承類中,所有虛基類及其上一層的基類的建構函式被呼叫;
虛函式表指標被初始化;
如果有成員初始化列表則將建構函式擴充套件;
執行使用者**。
形式化的說明,以下四種情況不會展現bitwise copy semantics:
當類中包含乙個類成員物件,而該物件宣告了以訛copy constructor;
當類繼承自乙個宣告了copy constructor的類;
當類宣告了至少乙個虛函式;
當類的繼承鏈中,其上層類中存在虛繼承。
建議盡可能不要允許乙個virtual base class的拷貝操作,甚至提供乙個比較奇怪的建議:不要在任何virtual base class 中宣告資料。 應為copy assignment opertor沒有什麼好的方法避免virtual base class 在派生層次中重複拷貝現象。
如果class內含的member class object(或其base class)含有析構函式時,編譯器才會合成出乙個析構函式,否則其他情況下都沒有必要合成它。
乙個有程式設計師定義的destructor被擴充套件的方式類是constructor被擴充套件的方式,但順序相反:
destructor的函式本身首先被執行,即使用者**先執行。
如果class擁有member class objects,而後者擁有destructor,那麼會以其宣告順序的相反順序被呼叫。
如果內帶乙個vptr,則現在重新設定,指向適當之base class的virtual table。(並總是設定,只用量中情況下設定,和建構函式相似)
如果有任何直接的nonvirtual base classes擁有destructor,他會以宣告順序相反的順序呼叫。
如果有任何virtual base class擁有destructor,而且是當前class是最尾端的class,那麼他們會以其原來的構造順序的相反順序被呼叫。
深入探索C 物件模型 七 構造
三個重要函式 建構函式,析構函式,拷貝建構函式。1.無繼承情況下的物件構造。當類中存在虛函式時,編譯器會對該類產生膨脹作用,例如如下類 cpp view plain copy class point virtual float z protected float x,y a.我們所定義的建構函式中,...
深入探索C 物件模型之物件
物件 一 在c語言中,資料 和 對資料的處理 函式 分開宣告的,也就是說,語言本身並沒有支援 資料和函式 之間的關聯性。例如,typedef struct point3dpoint3d 而在c 中,座標型別和座標數目都可以引數化 template class point type operator ...
深入探索C 物件模型
深入探索c 物件模型 本書目錄結構如下 第1章 關於物件 object lessons 加上封裝後的布局成本 layout costs for adding encapsulation 1.1 c 模式模式 the c object model 簡單物件模型 a object model 驅動物件模...