当派生类没有覆盖虚函数时,为什么需要vptr?

时间:2012-02-02 09:11:22

标签: c++ override virtual-functions vptr

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指针sizeofbaseder4的原因是什么?这里 VTABLE指针的要求是什么?

4 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:1)

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

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

显然编译器编写者没有发现这样做的麻烦。可能是因为它不会经常使用,并且因为您可以通过从基类中删除virtual来自己完成。

答案 2 :(得分:1)

编译器无法从'base'类中优化出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的原因。

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

答案 3 :(得分:0)

继承是is-a关系。 der是一个basebase的尺寸为4der的尺寸至少为4vftableptrbase的成员,它将是der的成员。

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