我正在通过Marshall Cline的C ++常见问题解答 - 特别是this link about how virtual functions are implemented in the compiler。
似乎是说派生类的vptr存在于对象的基类部分中。当创建派生类的实例时,不会在派生类部分中创建另一个vptr - 只需将基类部分中已存在的vptr初始化为指向正确的vtable。
我的问题是:如果我在派生类中声明一个虚函数,不在基类中,那么开销是多少?是否在派生类部分中创建了额外的vptr - 或者它是否仍然以相同的方式完成,即基类部分中的vptr被指定为指向特定的vtable?
所以 - 让我的问题更具体一点 - 在下面的例子中,编译器是否为Apple
类提供了额外的vtable,因为它添加了peel_me()
虚函数? (我假设答案肯定是肯定的)。如果是这样,编译器是否给Apple
另一个vptr的实例(即在它的Fruit
基类部分中的那个)?
class Fruit {
public:
virtual void display();
};
class Apple : public Fruit {
public:
virtual void display() { std::cout << "I'm an apple!\n"; }
virtual void peel_me(); // extra virtual function, that is not in the base class
};
我似乎无法在任何地方找到答案。
答案 0 :(得分:2)
典型地:
无论您是否添加Apple
, peel_me
都需要自己的vtable,因为它需要在display
的实例上通过虚拟调用找到Apple
的覆盖
添加peel_me
会使vtable中的一个条目大于其他条目 - 与peel_me
的代码相比,此附加条目可能占用或不占用大量空间,但是您需要期待可能没有。 Apple
的所有派生类的vtable也比没有peel_me
时更大的一个条目。
Apple
的实例只有一个vptr。它指向Apple
的vtable。此表包含Apple
的所有虚拟成员函数的条目,包括从Fruit
继承的函数,包括Apple
中未覆盖的任何函数(在这种情况下,Apple
中的vtable条目1}}指的是Fruit
)中的实现。
答案 1 :(得分:1)
如果基类已经有虚函数,则无。
只有层次结构链中声明的第一个virtual
函数会影响对象的大小,因为会添加指向虚拟表的指针。后续的没有。
这当然是依赖于实现的,但大多数都是这样的。
答案 2 :(得分:1)
编译器通常会创建一个vtable
来存储指向对象虚函数的指针。因此,添加一个通常是成本(假设基类已经具有虚拟函数,因此已经支付了存储vtable
的开销)另外一个函数指针的存储和解除引用的速度影响每次调用该虚函数的指针。
答案 3 :(得分:0)
没有绝对的规则,这取决于实施,
但是......如果你的派生类已经超载了至少一个
基类中的虚函数,它已经有了自己的
vtable
;添加虚拟功能将添加一个条目
vtable
(通常为4或8个字节)。但是从那以后
应该只是每个进程的vtable的一个实例,那就是
整个程序中的一个指针,在现代机器上,
可以有效地被认为是没有。