[Lang] 虛擬函式

yaoguyuan發表於2024-08-16

[Lang] 虛擬函式

1. 虛擬函式與純虛擬函式

多型分為靜態多型和動態多型,靜態多型指函式過載和運算子過載,而動態多型指虛擬函式。

動態多型的條件:

  • 子類重寫父類的虛擬函式
  • 父類指標或引用指向子類物件

虛擬函式的底層原理:

  • 對於每個包含虛擬函式的類,編譯器都會建立一個虛擬函式表(vftable),每一個虛擬函式表項指向對應的虛擬函式;該類的每個物件都有一個虛擬函式表指標(vfptr),指向所屬類的虛擬函式表。
  • 如果子類沒有重寫虛擬函式,那麼該虛擬函式表項將保留;如果子類重寫了虛擬函式,那麼該虛擬函式表項將更新;如果子類定義了新的虛擬函式,那麼該虛擬函式表將新增一個新的虛擬函式表項。
  • 當透過父類指標或引用呼叫虛擬函式時,程式會首先找到父類指標或引用指向的子類物件,然後透過子類物件中的(vfptr)找到對應的虛擬函式表(vftable),然後透過查詢虛擬函式表中的相應項找到虛擬函式的地址,最後進行函式呼叫。

包含純虛擬函式的類為抽象類,無法例項化物件;子類必須重寫抽象類的純虛擬函式,否則依然屬於抽象類。

#include<iostream>
using namespace std;

class Animal
{
public:
    virtual void speak() = 0;
};

class Dog : public Animal
{
public:
    virtual void speak()
    {
        cout << "I am a dog." << endl;
    }
};

int main()
{
    Animal *a = new Dog;
    a->speak();
    delete a;
    a = nullptr;
    return 0;
}
PS D:\CppDev\Lang\virtual_function> cd "d:\CppDev\Lang\virtual_function\" ; if ($?) { g++ test1.cpp -o test1 } ; if ($?) { .\test1 }
I am a dog.

2. 虛析構和純虛析構

當父類指標或引用指向堆區子類物件且子類存在堆區成員變數時,必須使用虛析構或純虛析構,避免懸空指標問題。

純虛析構必須在類外實現。

#include<iostream>
using namespace std;

class Animal
{
public:
    Animal()
    {
        cout << "Animal建構函式呼叫" << endl;
    }
    virtual ~Animal() = 0;
};

Animal::~Animal()
{
    cout << "Animal解構函式呼叫" << endl;
}

class Dog : public Animal
{
public:
    string *name;
    Dog(string name)
    {
        this->name = new string(name);
        cout << "Dog建構函式呼叫" << endl;
    }
    virtual ~Dog()
    {
        if (name != nullptr) delete name;
        cout << "Dog解構函式呼叫" << endl;
    }
};

int main()
{
    Animal *a = new Dog("Wangcai");
    delete a;
    a = nullptr;
    return 0;
}
PS D:\CppDev\Lang\virtual_function> cd "d:\CppDev\Lang\virtual_function\" ; if ($?) { g++ test2.cpp -o test2 } ; if ($?) { .\test2 }
Animal建構函式呼叫
Dog建構函式呼叫
Dog解構函式呼叫
Animal解構函式呼叫

相關文章