1. 程式人生 > >深度探索C++物件模型--之物件的差異加上多型之後

深度探索C++物件模型--之物件的差異加上多型之後

class zooAnimal{
public:
	zooAnimal();
	virtual ~zoonAnimal();
	virtual void rotate(); 
protected:
	int loc;
	string name;
};
class Bear :public zooAnimal{
public :
	Bear();
	~Bear();
	void rotate(); 
	void dance();
protected:
	enum Dances{};
	Dances dance_know;
};

zooAnimal za("zoey");
zooAnimal *pza = &za;
Bear  b("yogi");
Bear *pb = &b;
Bear &rb = *pb;

基類zooAnimal的物件佈局如下,假設指標pza的地址為1000;

                                基類物件za的記憶體佈局

加上多型之後派生類Bear的記憶體佈局如下(假設指標地址為1000):

                                  子類Bear的記憶體分配
一個Bear指標和一個zooAnimal指標有何不同之處?

Bear b;
zooAnimal *pz = &b;
Bear *pb =&b;

以上兩指標pz pb都指向Bear的物件的第一個byte,期間的差異是,pb所覆蓋的地址包含整個Bear object, 而pz所覆蓋的地址只包含Bear object 中的zooAnimal subobject.
除了zooAnimal subobject中出現的member你不能夠用pz來直接處理Bear的任何member,唯一是通過vritual機制。
當我們寫pz->rotate();時,pz的型別將在編譯時期決定以下幾點:
1、固定的可用介面,pz只能呼叫zooAnimal 的public介面。
2、該介面是zooAnimal的公共介面。
在每一個執行點,pz所指的object類可以決定rotate()所呼叫的實體。型別資訊的封裝不是維護於pz中,而是維護於link之中,此link存在於object的vptr和vptr所指的virtual table之間。

如下這種

Bear b;
zooAnimal za = b;//這會引起切割(sliced)
//呼叫zooAnimal::rotate()
za.rotate();

1、為什麼za呼叫的是zooAnimal實體而不是Bear實體呢?
za並不是(而且也絕不會是)一個Bear,它是一個zooAnimal,多型所造成的 “一個以上的型別” 的潛在力量,並不能夠實際發揮在 “直接存取 objects” 這件事上。有一個似是而非觀念:OO程式設計並不支援對object的直接處理。一個point 或一個reference之所以支援多型是因為它們並不引發記憶體中的任何委託操作,會受到改變的只是它們所指向的記憶體的”大小和內容解釋方式“而已。
然而任何試圖改變object za的大小,便會違反其定義中受契約保護的”資源需求量“。如果把整個Bear object指給za,則會溢位它所配置得到的記憶體。
當一個base class object 被直接初始化為一個derived class object時,derived object就會被切割(sliced),多型於是不再呈現。總而言之,多型是一種威力強大的設計機制,允許你繼承一個抽象的public介面之後,封裝相關的型別。C++通過class 的pointer和reference來支援多型,這種程式風格叫做 ”面向物件“。

2、如果初始化函式將一個object內容拷貝到另一個object中去,為什麼za的vptr不指向Bear的vritual table?
編譯器在初始化及指定操作之間做了仲裁,編譯器必須確保某個object含有一個或一個以上的vptrs的內容不會被base class object初始化所改變。