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指针,sizeof
类base
或der
为4
的原因是什么?这里 VTABLE指针的要求是什么?
答案 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
是一个base
。 base
的尺寸为4
,der
的尺寸至少为4
。 vftableptr
是base
的成员,它将是der
的成员。
base
有一个虚方法,因此它将有一个指向虚拟表的指针,无论你是否使用它。