📜  C ++ |虚函数|问题14(1)

📅  最后修改于: 2023-12-03 14:59:36.909000             🧑  作者: Mango

C++ | 虚函数 | 问题14

在 C++ 中,虚函数是一种非常重要的概念。这些函数可以在派生类中被覆盖,从而实现多态性。

什么是虚函数

在 C++ 中,虚函数是一种可以在派生类中被覆盖的基类成员函数。当使用基类指针或引用调用虚函数时,运行时系统会根据实际对象类型动态地调用相应的派生类函数,这种机制称为多态性。

虚函数的声明方式如下:

class Base {
public:
    virtual void func();
};

class Derived : public Base {
public:
    virtual void func(); // 覆盖了基类的虚函数
};

注意到上面的函数是用 virtual 关键字进行声明的。这个关键字告诉编译器这个函数是一种虚函数。如果派生类中的函数签名与基类中的虚函数不同,那么被覆盖的函数就不再是虚函数。

虚函数实现原理

在 C++ 中,虚函数的实现是通过虚函数表(VTable)和虚函数指针(VTptr)来实现的。具体来说:

  1. 对于每个有虚函数的类,编译器都会在该类的构造函数中生成一个指向虚函数表的指针(VTptr)。这个指针被添加到对象的结构中。
  2. 每个类有一个虚函数表(VTable),VTable 中存储了该类中所有虚函数的地址。
  3. 派生类中新的虚函数会放在原有虚函数的后面。如果派生类没有覆盖某个虚函数,那么它的虚函数表中会存储基类中对应虚函数的地址。如果派生类覆盖了某个虚函数,那么它的虚函数表中就会存储覆盖后的新地址。
  4. 调用虚函数时,程序会通过指向对象的 VTptr 找到对应的虚函数表,然后根据虚函数在表中的索引,直接调用相应的函数。

虚函数的实现机制非常复杂,但是对于程序员来说,只需要知道虚函数实现多态的原理即可。

虚函数的重载和覆盖

在 C++ 中,派生类中的虚函数可以有不同的函数签名(参数类型、参数个数等等),但是函数名称和返回值类型必须与基类中的虚函数完全一致。

在派生类中,我们可以通过 virtual 关键字来覆盖基类中的虚函数:

class Base {
public:
    virtual void func();
};

class Derived : public Base {
public:
    virtual void func(); // 覆盖了基类的虚函数
};

注意到,在派生类中覆盖基类中的虚函数,必须指定该函数为 virtual

此外,派生类中也可以重载继承来的虚函数,重载虚函数的方式和普通函数一样:

class Base {
public:
    virtual void func();
};

class Derived : public Base {
public:
    virtual void func(int); // 重载了基类的虚函数
};

注意到,在派生类中重载基类中的虚函数,不需要指定该函数为 virtual

虚析构函数

在 C++ 中,如果一个类含有虚函数,那么它的析构函数也应该是虚函数。这是因为,如果派生类中有指向基类对象的指针,那么使用 delete 来销毁这个指针时,只会调用基类的析构函数。如果这个析构函数不是虚函数,那么就无法调用派生类的析构函数,导致内存泄漏。

因此,正确的做法是使用虚析构函数:

class Base {
public:
    virtual ~Base();
};

class Derived : public Base {
public:
    virtual ~Derived();
};

注意到,析构函数也符合虚函数的覆盖和重载规则。

总结

在 C++ 中,虚函数是实现多态的重要机制。虚函数的使用需要理解虚函数表和虚函数指针的原理。派生类中的虚函数可以覆盖基类中的虚函数,也可以重载继承来的虚函数。如果一个类含有虚函数,那么它的析构函数也应该是虚函数。