point類型·
指針的類型
不同類型的指針,從內存需求的觀點來說,沒有什麽不同!他們三個都需要足夠的內存來繁殖一個機器地址,“指向不同類型之各指針”之間的差異,既不在其指針表示法不同,也不再其內容(代表一個地址)不同,而是在其所尋址出來的對象類型不同。也就是說,“指針類型”會導致編譯器如何解釋某個特定地址中的內存內容及其大小
1、 一個指向地址1000的整數地址,在32位機器上,將涵蓋地址空間1000~1003
2、 那麽,一個指向地址1000而類型為void*的指針,將涵蓋怎樣的地址空間呢?這個不知道,這就是為什麽一個類型為void*的指針只能夠含有一個地址,而不能通過它操作所指之對象的緣故。
其實轉型是一種編譯器指令,大部分情況下它並不改變一個指針所含的真正地址,它只影響“被指出之內存的大小和其內容”的解釋方式。
Class ZooAnimal{
Public:
ZooAnimal();
Virtual~ZooAnimal();
Virtualvoid rotate();
Protected:
Intloc;
Stringname;
};
Class bear:public ZooAnimal
{
Public:
Bear();
~Bear()
Voidrotate();
Viratul void dance();
Protected:
Int cell_block;
};
但是考慮多態以後呢?加入Bear繼承了ZooAnimal 同時是Public繼承
Bear b;
ZooAnimal *pz = &b;
Bear* pb = &b;
他們每個都指向Bear對象的第一個字節,其間的差別是,pb所涵蓋的地址包含整個Bear對象,而pz所涵蓋的地址只包含Bear對象中的ZooAnimal子對象
除了在ZooAnimal中出現的成員,你不能夠實用pz來直接處理Bear的任何成員。唯一例外的是通過virtual機制。
Pz->cell_block;
//不正確,cell_block不是ZooAnimal的一個成員,雖然我們知道pz當前指向一個Bear對象
((Bear*)pz)->cell_block;
//合法 經過一個明白的轉型操作就沒有問題
Pb->cell_block;
//合法 因為cell_block是Bear的一個成員
但是當我們寫
Pz->rotate();時,(rotate是虛擬函數)pz的類型將在編譯使其決定一下兩點:
固定的可用接口,也就是說,pz只能夠調用ZooAnimal的Public接口
該接口的訪問級別(例如rotate()是ZooAnimal的一個public成員)
在每一個執行點,pz所指的對象類型可以決定rotate()所調用的實體,類型信息的封裝並不是維護與pz之中,而是維護與link之中,此Link存在於對象的vptr和vptr所指的virtual table之間,在每一個virtual table中都有一個信息是type of info的信息
Bear b;
ZooAnimal za = b; //這樣會引起切割
Za.rotate(); //調用ZooAnimal::rotate()
為什麽rotate()所調用的是ZooAnimal實體而不是Bear實體?為什麽za的vptr不指向Bear的virtual table
編譯器在初始化以及賦值操作(將一個對象賦值給另一個對象)之間做了仲裁,編譯器必須確保如果某個對象含有一個或一個以上的vptrs,那麽vptrs的內容不會被基類對象初始化或改變
加入ZooAnimal->Bear->Panda 繼承關系
ZooAnimal za;
ZooAnimal * pza;
Bear b;
Panda* pp = new Panda;
Pza = *b
將za或b的地址,或pp所含的內容(也是個地址)指定給pza,顯然不是問題,一個指針或一個引用值所以支持多態,是因為它們並不引發內存中任何“與類型有關的內存委托操作”,會受到改變的只是它們所指向的內存的“大小和內容解釋方式”而已。
所謂與類型有關的內存委托操作是指這個類型被編譯所認識的大小,比如上面使用pz->cell_block.
大小和內容解釋方式是在編譯期間發生的,但是對於多態來說,是一個函數,但是函數是不依附於對象的,如果只要訪問的對象中存在這個函數並且這個函數的訪問級別可以達到,編譯期間就可以實現,但是等到運行期間,如果發現這個函數是一個虛函數,這個時候在虛函數表中的第一個位置就是關於這個類型的所有信息,這個時候就會知道這個對象對應的真實類型是什麽。需要分清這兩點的差異
point類型·