歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++中虛函數工作原理和(虛)繼承類的內存占用大小計算

C++中虛函數工作原理和(虛)繼承類的內存占用大小計算

日期:2017/3/1 10:08:08   编辑:Linux編程

一、虛函數的工作原理

虛函數的實現要求對象攜帶額外的信息,這些信息用於在運行時確定該對象應該調用哪一個虛函數。典型情況下,這一信息具有一種被稱為 vptr(virtual table pointer,虛函數表指針)的指針的形式。vptr 指向一個被稱為 vtbl(virtual table,虛函數表)的函數指針數組,每一個包含虛函數的類都關聯到 vtbl。當一個對象調用了虛函數,實際的被調用函數通過下面的步驟確定:找到對象的 vptr 指向的 vtbl,然後在 vtbl 中尋找合適的函數指針。

虛擬函數的地址翻譯取決於對象的內存地址,而不取決於數據類型(編譯器對函數調用的合法性檢查取決於數據類型)。如果類定義了虛函數,該類及其派生類就要生成一張虛擬函數表,即vtable。而在類的對象地址空間中存儲一個該虛表的入口,占4個字節,這個入口地址是在構造對象時由編譯器寫入的。所以,由於對象的內存空間包含了虛表入口,編譯器能夠由這個入口找到恰當的虛函數,這個函數的地址不再由數據類型決定了。故對於一個父類的對象指針,調用虛擬函數,如果給他賦父類對象的指針,那麼他就調用父類中的函數,如果給他賦子類對象的指針,他就調用子類中的函數(取決於對象的內存地址)。

虛函數需要注意的大概就是這些個地方了,之前在More effective C++上好像也有見過,不過這次在Visual C++權威剖析這本書中有了更直白的認識,這本書名字很牛逼,看看內容也就那麼回事,感覺名不副實,不過說起來也是有其獨到之處的,否則也沒必要出這種書了。

每當創建一個包含有虛函數的類或從包含有虛函數的類派生一個類時,編譯器就會為這個類創建一個虛函數表(VTABLE)保存該類所有虛函數的地址,其實這個VTABLE的作用就是保存自己類中所有虛函數的地址,可以把VTABLE形象地看成一個函數指針數組,這個數組的每個元素存放的就是虛函數的地址。在每個帶有虛函數的類 中,編譯器秘密地置入一指針,稱為v p o i n t e r(縮寫為V P T R),指向這個對象的V TA B L E。 當構造該派生類對象時,其成員VPTR被初始化指向該派生類的VTABLE。所以可以認為VTABLE是該類的所有對象共有的,在定義該類時被初始化;而VPTR則是每個類對象都有獨立一份的,且在該類對象被構造時被初始化。

通過基類指針做虛函數調 用時(也就是做多態調用時),編譯器靜態地插入取得這個V P T R,並在V TA B L E表中查找函數地址的代碼,這樣就能調用正確的函數使晚捆綁發生。為每個類設置V TA B L E、初始化V P T R、為虛函數調用插入代碼,所有這些都是自動發生的,所以我們不必擔心這些。

  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. virtual void fun1()
  7. {
  8. cout << "A::fun1()" << endl;
  9. }
  10. virtual void fun2()
  11. {
  12. cout << "A::fun2()" << endl;
  13. }
  14. };
  15. class B : public A
  16. {
  17. public:
  18. void fun1()
  19. {
  20. cout << "B::fun1()" << endl;
  21. }
  22. void fun2()
  23. {
  24. cout << "B::fun2()" << endl;
  25. }
  26. };
  27. int main()
  28. {
  29. A *pa = new B;
  30. pa->fun1();
  31. delete pa;
  32. system("pause");
  33. return 0;
  34. }
Copyright © Linux教程網 All Rights Reserved