近來看了侯捷的《深刻淺出MFC》,讀到C++重要性質中的虛函數與多態那部份內容時,頓時有了疑惑。由於書中說了這麼一句:使用「基類之指針」指向「派生類之對象」,由該指針只能調用基類所定義的函數,若是要讓基類的指針使用派生類中定義的函數,就將該函數定義爲虛函數。ios
但在「Object slicing與虛函數」這一小節給出了一個及其經典的例子,它指出,在向上(即向基類)強制轉型時,會形成對象內容的被切割。web
下面用示例進行說明:函數
1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; 4 5 class A 6 { 7 public: 8 virtual void fn(){cout<<"A fn"<<endl;} 9 }; 10 11 class B: public A 12 { 13 public: 14 virtual void fn(){cout<<"B fn"<<endl;} 15 }; 16 17 int main(int argc, char* argv[]) 18 { 19 B b1; 20 A a1=(A)b1; 21 A * a2=(A*)&b1; 22 a1.fn(); 23 a2->fn(); 24 return 0; 25 }
結果以下:spa
經過調試分析其內存模型以下:指針
可知,經過A a1=(A)b1傳值時,對象中指向虛函數表的指針__vfptr值不一樣,可是經過A *a2=(A*)&b1傳址操做時,對象中指向虛函數表的指針__vfptr值是相同的,調用的是類B對象的虛函數表中虛函數fn()。注意,對象調用的是哪一個類的虛函數就要看類對象的虛函數表中的函數是哪個。這句話也好理解,a1虛函數表中fn()是A::fn(),a2虛函數表中fn()是B::fn(),因此出現以上結果。調試
在以下包含父類的父類的繼承中:code
1 class A 2 { 3 public: 4 virtual void fn(){cout<<"A fn"<<endl;} 5 }; 6 7 class B: public A 8 { 9 public: 10 virtual void fn(){cout<<"B fn"<<endl;} 11 }; 12 13 class C: public B 14 { 15 public: 16 }; 17 C c; 18 A a1=(A)c; 19 A * a2=(A*)&c; 20 a1.fn(); 21 a2->fn();
這時,a1虛函數表中fn()爲A::fn(),a2虛函數表中fn()爲B::fn(),由於類C對象的虛函數表中fn()爲B::fn(),a2中指向虛函數表的指針值與類C對象指向虛函數表的指針值相同。orm
侯捷的書中有句話是這麼對它進行解釋的:因爲(A)b1.fn()是傳值而非傳地址操做,編譯器以所謂的拷貝構造函數把A對象內容複製了一份,使得b1的虛函數表內容與A對象的虛函數表內容相同。對象
看來,得多讀聖賢書,讀懂讀透啊!blog