当派生类不重写虚函数时,为什么需要 vptr?

Why is a vptr required when the derived class doesn't override the virtual function?

本文关键字:为什么 vptr 函数 派生 重写      更新时间:2023-10-16
class base {
public:
    void virtual fn(int i) {
        cout << "base" << endl;
    }
};
class der : public base{
    public:
    void  fn(char i) {
        cout << "der" << endl;
    }
};
int main() {
    base* p = new der;
    char i = 5;
    p->fn(i);
    cout << sizeof(base);
    return 0;
}

这里base类中定义的函数 fn 的签名与类中定义的函数fn()的签名不同der尽管函数名相同。因此,der类中定义的函数隐藏base类函数fn()。因此,类 der 版本的 fn 不能通过p->fn(i)调用来调用;没事。

我的观点是,如果不使用 VTABLE 指针,为什么sizeofbaseder 4?这里的VTABLE指针有什么要求?

请注意,这是高度依赖于实现的,并且可能因每个编译器而异。

存在 vtable 的要求是 Base 类用于继承和扩展,从它派生的类可能会重写该方法。

Base

和 Derived 这两个类可能驻留在不同的翻译单元中,编译 Base 类时的编译器不会真正知道该方法是否会被覆盖。因此,如果它找到关键字virtual它会生成 vtable .

vtable 通常不仅用于虚函数,而且还用于在执行某些dynamic_cast或程序访问类的type_info时标识类类型。

如果编译器检测到没有虚拟函数被覆盖并且没有使用任何其他功能,它只是可以删除 vtable 指针作为优化。

显然,编译器编写者发现这样做不值得麻烦。可能是因为它不会经常使用,并且您可以通过从基类中删除virtual来自己执行此操作。

编译器

无法从"基"类中优化出vtable成员变量,因为同一项目中可能存在另一个源文件或另一个包含以下内容的项目:

struct ived : base {
    ived() : p(new char[BIG_DATA_SIZE]) {}
    virtual ~ived();
    virtual void fn(int);
private:
    char* p;
};

析构函数和fn可以在其他地方实现:

ived::~ived() { delete[] p; }
void ived::fn(int) {
    cout << "ived" << endl;
}

在另一个地方的某个地方可能有这样的代码:

base* object = new ived;
ived->fn(0);
delete object;
cout << sizeof(base) << endl;

因此,将有两个问题:未调用虚拟函数ived::fn,未调用虚拟析构函数,因此BIG_DATA_SIZE不删除。否则,这里的sizeof(base)会有所不同。这就是为什么编译器总是为具有虚拟成员函数或虚拟基类的任何类生成vtable的原因。

关于在派生类中调用析构函数,必须将其视为必须的:如果您有任何具有任何虚函数的类,则该类还应声明虚拟析构函数。

继承

是一种is-a关系。 derbase . base有大小4der至少会有大小4vftableptrbase的成员,它将是der的成员。

base有一个虚拟方法,因此无论您是否使用它,它都将具有指向虚拟表的指针。