首頁 > 軟體

關於虛擬函式實現多型的原理及分析

2023-09-08 22:00:42

1、C++中如何實現多型

  • 基礎類別中先宣告一個虛擬函式
  • 至少有一個繼承該基礎類別的子類

2、虛擬函式實現多型的原理

  • 當一個類中出現虛擬函式或著子類繼承了虛擬函式時,就會在該類中產生一個虛擬函式表(virtual table),虛擬函式表實際上是一個函數指標陣列(在有的編譯器作用下是連結串列),裡面的每一個元素對應指向該類中的某一個虛擬函式的指標。
  • 被該類宣告的物件會包含一個虛擬函式表指標(virtual table pointer),指向該類的虛擬函式表的地址。
  • 虛擬函式的呼叫過程: 當一個物件要呼叫到虛擬函式時,先將物件記憶體中的vptr指標(虛擬函式表指標)指向定義該類的vtbl(虛擬函式表),vtbl再尋找裡面的指標指向想要呼叫的虛擬函式,從而完成虛擬函式的呼叫。

2.1 單類繼承

定義一個父類別

class Person{
public:
	virtual void f(){cout << "use f()" << endl;}
	virtual void g(){cout << "use g()" << endl;}
	virtual void h(){cout << "use h()" << endl;}
};

父類別物件其在記憶體中佈局如下:

  • 再定義一個子類,此時並不覆蓋父類別的虛擬函式:
class Bag:public Person{
public:
	virtual void i(){cout << "use i()" << endl;}
	virtual void j(){cout << "use j()" << endl;}
};

可以看出虛擬函式表內的虛擬函式是按宣告順序進行排序的

父類別虛擬函式排在子類虛擬函式之前

  • 當我們把子類中的虛擬函式覆蓋掉:(修改Bag類)
class Bag:public Person{
public:
	void f(){cout << "class Bag use fun" << endl;}
	virtual void i(){cout << "use i()" << endl;}
	virtual void j(){cout << "use j()" << endl;}
}; 

子類覆蓋的虛擬函式,放在父類別原先放該虛擬函式的位置上。

所以當父類別指標指向該子類物件時,會呼叫該子類的過載函數

2.2 多類繼承

  • 子類沒有覆蓋父類別的虛擬函式

子類的虛擬函式放在第一張虛擬函式表中,緊跟著第一個父類別的虛擬函式

如果每個父類別都有虛擬函式,則有幾個父類別就有幾張虛擬函式表

  • 子類覆蓋父類別的虛擬函式

父類別的虛擬函式被子類覆蓋後,則該子類對應的過載函數的位置在被覆蓋的父類別函數的位置上。(如果父類別沒有該虛擬函式,則不用被覆蓋)

父類別的虛擬函式被子類覆蓋後,則父類別指標指向該子類物件時,呼叫的f()便是子類中過載的f()

範例

#include <iostream>
using namespace std;

class Person1{
public:
    virtual void f(){}
    virtual void g(){}
    virtual void h(){}
    virtual ~Person1(){}
};

class Person2{
public:
    virtual void f(){}
    virtual void g(){}
    virtual void h(){}
    void a(){           // 成員函數,不需要過載
        cout << "class Person2" << endl;
    }
    virtual ~Person2(){}
};

class Person3{
public:
    virtual void g(){}
    virtual void h(){}
    virtual ~Person3(){}
};

class Bag:public Person1, public Person2, public Person3{
public:
    void f(){
        cout << "Bag f()" << endl;
    }
    void g(){
        cout << "Bag g()" << endl;
    }
    void a(){
        cout << "Class Bag" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Person3* p3 = new Bag;
    //p3->f();    // P3 沒有成員函數f()
                  // 多型首先得是 父類別有虛擬函式,其次是子類要定義該函數的過載
                  // 如果父類別的虛擬函式改為成員函數,則子類無法進行過載,即無法實現多型
    delete p3;
    p3 = NULL;

    Person1* p1 = new Bag;
    p1->f();
    delete p1;
    p1 = NULL;

    Person2* p2 = new Bag;
    p2->a();
    delete p2;
    p2 = NULL;
    
    return 0;
}

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com