1. 程式人生 > >C++中虛函數的動態綁定和多態性

C++中虛函數的動態綁定和多態性

gif alt eric 可能 運行 lan event 重要 ostream

目錄

    • 靜態類型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 }

二.虛函數的的實現機制

二、C++多態性

1.多態性的定義:同一操作作用於不同的對象,可以有不同的解釋,即產生不同的執行效果。多態性有兩種,編譯時的多態,也就是函數重載;運行時多態,在系統運行時確定,是通過虛成員實現的。一般指的是後者

2.典型用例

技術分享
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Person
 5 {
 6 public:
 7     virtual void print(){
 8         std::cout << "I‘m a person" << endl;
 9     }
10    
11 };
12 
13 class Chinese : public Person
14 {
15 public:
16     virtual void print(){
17         std::cout << "I‘m as Chinese" << endl;
18     }
19    
20 };
21 
22 class American : public Person
23 {
24 public:
25     virtual void print(){
26         std::cout << "I‘m as American" << endl;
27     }
28    
29 };
30 
31 
32 //reference 
33 void printPerson(Person& person){
34     person.print();
35 }
36 //pointer 
37 void printPerson(Person* p){
38     p->print();
39 }
40 int main()
41 {
42     Person p;
43     Chinese c;
44     American a;
45     printPerson(p);
46     printPerson(c);
47     printPerson(a);
48 
49     printPerson(&p);
50     printPerson(&c);
51     printPerson(&a);
52     
53     return 0;
54 }
View Code

4.多態的實現:多態是由繼承和虛函數實現的,因為虛函數是動態綁定的,依賴於調用對象(靜態類型為基類的指針和引用)的動態類型,所以根據動態類型的不同,而導致操作不同,也就是多態性。

3.虛函數的實現:簡單的說虛函數是通過虛函數表來實現的。

每個帶有虛函數的類,都會有一個虛函數表(vtbl),表中的每一項記錄它一個的虛函數的地址。實際上一個函數指針的數組。類的對象的最前面存儲虛函數表的地址。

虛函數表在類的繼承中也會繼承和重寫,當有重寫發生時,就會產生多態性。

 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 }

Person 類的vtbl : Person::print()的地址,Person::foo()的地址

Chinese類的地址:Chinese::print()的地址,Person::foo()的地址,Person::foo1()的地址

American類的地址:American::print()的地址,Person::foo()的地址

三、總結

  多態是C++的三大特性之一,非常重要,產生的條件是繼承關系,基類中存在虛成員函數,派生類override(重寫覆蓋)基類的的虛成員函數,在代碼上表現為基類對象的指針或引用調用虛成員函數,運行結果表現為實際調用各自派生類重寫的函數。

多態的核心虛成員函數是動態綁定的,即依賴於對象的動態類型。理解多態的難點是虛函數的實現機制,即虛函數表。

參考:http://blog.csdn.net/chgaowei/article/details/6427731

C++中虛函數的動態綁定和多態性