目錄
-
- 靜態類型 vs 動態類型、靜態綁定 vs 動態綁定
- 虛函數動態綁定實現機制、虛析構函數
- 多態性
一.靜態 vs 動態
靜態類型 VS 動態類型。靜態類型指的是對象聲明的類型,在編譯器確定的。動態類型指的是對象的所指向的類型,動態類型是可以更改的,靜態類型無法更改。繼承關系會導致對象的指針和引用具有靜態類型和動態類型,因為繼承關系的存在中可能會存在類型之間的向上向下類型轉換。靜態綁定 VS 動態綁定。某特性(函數)依賴與對象的靜態類型,在編譯期確定,某特性(函數)依賴於對象的動態類型,在運行期確定。只有通過基類的引用或者指針調用虛函數時,才能發生動態綁定,如果使用對象來操作虛函數的話,仍然會采用靜態綁定的方式。因為引用或者指針既可以指向基類對象,也可以指向派生類對象的基類部分,用引用或者指針調用的虛函數。例如下面的例子中,pd的靜態類型是D*,pb的靜態類型是B*,是在編譯器確定的;pd和pb的動態類型都是D*。DoSomething()普通的成員函數是在編譯器靜態綁定的,調用各自的靜態類型中的方法。vfun()是虛成員函數,虛函數是動態綁定的,因此調用動態類型的方法,也就是D類型中vfun。
1 #include <iostream> 2 using namespace std; 3 4 class B 5 { 6 public: 7 void DoSomething(){ 8 std::cout << "B::DoSomething()" << endl; 9 } 10 virtual void vfun(){ 11 std::cout << "B::vfun()" << endl; 12 } 13 }; 14 15 class D : public B 16 { 17 public: 18 void DoSomething(){ 19 std::cout << "D::DoSomething()" << endl; 20 } 21 virtual void vfun(){ 22 std::cout << "D::vfun()" << endl; 23 24 } 25 }; 26 27 int main() 28 { 29 D* pd = new D(); //指針存在注意靜態類型和動態類型 30 B* pb = pd; //指正注意靜態類型和動態類型 31 pd->DoSomething(); //普通成員函數,靜態綁定靜態類型中的方法 32 pb->DoSomething(); //普通成員函數,靜態綁定靜態類型中的方法 33 34 pd->vfun(); //虛成員函數,動態綁定到動態類型中的方法 35 pb->vfun(); //虛成員函數,動態綁定到動態類型中的方法 36 return 0; 37 }
二.虛函數動態綁定的實現機制
對象的指針或者引用調用虛函數的行為是動態綁定。虛函數是通過虛函數表來實現的。對於每一個含有虛函數的類,編譯器會為其創建一個虛函數表Vtable,虛函數表中記錄JMP指令的地址。類的虛函數表被該類的所有對象共享,編譯器會為每個虛函數的實例分配指向該類的虛函數表的指針。當一個含有虛成員函數的類被繼承后,器派生類中的同名函數會自動的成為虛函數,因此子類在重寫這個虛函數時,可以添加,也可以不加virtual關鍵字,習慣上會加上virtual使得程序更加的清晰。父類指針調用虛成員函數是多態在代碼層面的體現,這在運行過程中,父類指針將動態綁定(關聯)到具體的某個虛函數。指的是在運行期根據父類指針的動態類型的虛函數表來綁定到具體的函數。含有虛函數的類記住使用虛析構函數,否則容易導致派生類的資源無法正常釋放。例如下面情況。如果想進一步了解虛函數表需要了解,C++的對象模型,建議查看《C++中對象模型》。
1 #include <iostream> 2 using namespace std; 3 4 class B 5 { 6 public: 7 void DoSomething(){ 8 std::cout << "B::DoSomething()" << endl; 9 } 10 virtual void vfun(){ 11 std::cout << "B::vfun()" << endl; 12 } 13 14 ~B(){ 15 std::cout << "~B()" << std::endl; 16 } 17 }; 18 19 class D : public B 20 { 21 public: 22 void DoSomething(){ 23 std::cout << "D::DoSomething()" << endl; 24 } 25 virtual void vfun(){ 26 std::cout << "D::vfun()" << endl; 27 28 } 29 ~D(){ 30 std::cout << "~D()" << std::endl; 31 } 32 }; 33 34 int main() 35 { 36 B* pb = new D(); 37 pb->vfun(); 38 delete pb; // ~B(); 39 return 0; 40 }
三 C++多態性體現
C++多態性代碼層面體現的是父類的指針或引用調用虛成員函數。多態產生有三個條件:產生的條件是繼承關系、基類中含有虛成員函數、派生類重寫基類的虛成員函數。當用父類指針或引用調用虛成員函數,這個調用關系在運行器可能綁定(關聯)到不同的子類,產生不同的行為。
1 #include <iostream> 2 using namespace std; 3 class Person 4 { 5 public: 6 virtual void print(){ 7 std::cout << "I'm a person" << endl; 8 } 9 virtual void foo(){} 11 }; 12 13 class Chinese : public Person 14 { 15 public: 16 virtual void print(){ 17 std::cout << "I'm as Chinese" << endl; 18 } 19 virtual void foo2(){} 20 }; 21 class American : public Person 22 { 23 public: 24 virtual void print(){ 25 std::cout << "I'm as American" << endl; 26 } 27 }; 28 //reference 29 void printPerson(Person& person){ 30 person.print(); 31 } 32 //pointer 33 void printPerson(Person* p){ 34 p->print(); 35 } 36 int main() 37 { 38 Person p; 39 Chinese c; 40 American a; 41 printPerson(&p); 42 printPerson(&c); 43 printPerson(&a); 44 return 0; 45 }