在C語言中,數據和操作是分開聲明的。C++將數據和操作封裝在一起。
C++的數據成員包含在類對象中,成員函數不包含在類對象中。非內聯函數只產生一個函數實例,內聯函數在每一個調用處展開。C++封裝數據和操作帶來的開銷主要由虛函數機制和虛基類函數引起的。還有一些多重繼承下的額外負擔。
C++有靜態和非靜態兩種數據成員,靜態,非靜態和虛擬三種成員函數。
簡單對象模型爲每一個數據成員和成員函數分配一個槽,每個槽中有指針指向數據成員或者成員函數。對象中的成員以槽的索引值來尋址。該模型並沒有實際應用,但索引和槽的概念被用於後面的模型。
表格驅動對象模型爲對象分配兩個槽,分別指向數據成員表格和成員函數表格。成員函數表格中存儲了相應的成員。成員函數表格是一些槽,這些槽又分別指向各自的成員函數。這個模型也沒有應用,但是成員函數表的概念應用到了虛函數上。
C++對象模型將非靜態數據成員放在對象內,靜態數據成員放在對象外,靜態成員函數和非靜態成員函數也放在對象外,虛函數則在對象中放置一個指針vptr,該指針指向一個虛表vtbl,該虛表中存放的是相應虛函數的指針。vptr的設定和修改由類的ctor,dtor和assign自動完成。虛表中的第一個槽用來存放指向type_info對象的指針,該對象用來支持RTTI。
C++對象模型的優點是空間和存取時間的效率,缺點是如果類的非靜態數據成員有所修改,應用程序的代碼也要重新編譯。
在虛擬繼承下,基類不管被派生類多少次,永遠只有一個實例存在。繼承的對象模型在3.4節。
假設類X有一個cctor,vdtor和虛擬foo成員函數,那麼下面的代碼:
X foobar(){
X xx;
X*px=new X;
xx.foo();
px->foo();
delete px;
return xx;
}
可能被轉換爲:
void foobar(X&_result){
_result.X::X();
px=_new(sizeof(X));
if(px!=0)
px->X::X();
foo(&_result);
(*px->vtbl[2])(px);
(*px->vtbl[1])(px);
_delete(px);
return;
}
struct和class的區別是struct的訪問控制默認是public,class的訪問控制默認是private。
把一個子類對象賦給一個父類變量,會發生對象切割。
多態只存在於public類體系中,非public的派生,void*指針不支持。
多態可以把派生類的指針賦給父類指針變量。也可以通過dynamic_cast和typeid運算符:
circle*pc=dynamic_cast<circle*>(ps);
多態的用途是通過一個共同的接口來影響類型的封裝,這個接口通常定義在一個抽象的基類中。
類對象大小:
1.非靜態數據成員大小
2.對齊填充的大小
3.vptr指針
指針本身的大小是固定的。本質上,一個引用通常是以一個指針來實現的。
void*類型的指針變量只能有一個地址,不能通過它操作所指的對象,因爲不知道該對象的地址空間。所以要顯式轉換,指明所要訪問的地址空間的大小。
轉換cast是一種編譯器指令,它並不 一個指針所含的真正地址,隻影響該段內存的大小和內容的解釋方式。