[Lang] 虚函数

[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析构函数调用
posted @ 2024-08-16 00:26  yaoguyuan  阅读(12)  评论(0编辑  收藏  举报