编译器究竟在什么时候创建虚拟函数表?
1)当类至少包含一个虚函数时。
或
2)当直接基类包含至少一个虚函数时。
或
3)当层次结构的任何级别的任何父类包含至少一个虚拟函数时。
与此相关的一个问题是:在C++层次结构中放弃动态调度是可能的吗?
例如,考虑下面的例子。
#include <iostream>
using namespace std;
class A {
public:
virtual void f();
};
class B: public A {
public:
void f();
};
class C: public B {
public:
void f();
};哪些类将包含V表?
由于B没有将f()声明为virtual,那么C类是否获得动态多态性?
发布于 2009-12-27 02:22:52
除了"vtables是特定于实现的“(它们是特定的)之外,如果使用了vtable :每个类都将有唯一的vtable。尽管B::f和C::f没有声明为virtual,但因为在基类(在代码中为A)的虚方法上有匹配的签名,B::f和C::f都是隐式虚的。因为每个类至少有一个惟一的虚方法(B::f覆盖B实例的A::f,C实例的C::f类似地覆盖A::F),所以您需要三个vtable。
你一般不应该担心这样的细节。重要的是你是否有虚拟分派。通过显式指定要调用的函数,你不必使用虚拟分派,但这通常只在实现虚拟方法时有用(比如调用基的方法)。示例:
struct B {
virtual void f() {}
virtual void g() {}
};
struct D : B {
virtual void f() { // would be implicitly virtual even if not declared virtual
B::f();
// do D-specific stuff
}
virtual void g() {}
};
int main() {
{
B b; b.g(); b.B::g(); // both call B::g
}
{
D d;
B& b = d;
b.g(); // calls D::g
b.B::g(); // calls B::g
b.D::g(); // not allowed
d.D::g(); // calls D::g
void (B::*p)() = &B::g;
(b.*p)(); // calls D::g
// calls through a function pointer always use virtual dispatch
// (if the pointed-to function is virtual)
}
return 0;
}一些具体的规则可能会有所帮助,但不要引用我的话,我可能遗漏了一些边缘情况:
请记住,vtable类似于类的静态数据成员,实例只有指向它们的指针。
还可以参阅Jan Gray.撰写的综合文章 (1994年3月)(如果该链接失效,请单击Try Google。)
重用vtable的示例:
struct B {
virtual void f();
};
struct D : B {
// does not override B::f
// does not have other virtuals of its own
void g(); // still might have its own non-virtuals
int n; // and data members
};特别要注意的是,B的dtor不是虚拟的(这可能是实际代码中的一个错误),但在本例中,D实例将指向与B实例相同的vtable。
发布于 2009-12-27 02:08:46
答案是,“视情况而定”。这取决于你所说的“包含一个vtbl”是什么意思,也取决于特定编译器的实现者所做的决定。
严格地说,没有“类”包含虚函数表。某些类的某些实例包含指向虚拟函数表的指针。然而,这只是语义的一种可能的实现。
在极端情况下,编译器可以假设将一个唯一的数字放入实例中,该实例被索引到用于选择适当的虚拟函数实例的数据结构中。
如果你问,‘GCC是做什么的?’或者“可视化C++是做什么的?”然后你就可以得到一个具体的答案。
@Hassan Syed的答案可能更接近你所问的问题,但在这里保持概念的直截了当是非常重要的。
有行为(基于新类的动态调度)和实现。你的问题使用了实现术语,尽管我怀疑你是在寻找行为的答案。
行为上的答案是这样的:任何声明或继承虚拟函数的类都将在调用该函数时表现出动态行为。任何不这样做的类都不会。
在实现方面,编译器被允许做任何它想要实现的结果。
发布于 2009-12-27 02:11:29
应答
当类声明包含虚函数时,将创建vtable。当父级--在父级结构中的任何地方--有一个虚拟函数时,就会引入vtable,让我们称这个父级为Y。Y的任何父级都不会有vtable (除非他们的父级结构中有某个其他函数的virtual )。
继续阅读以进行讨论和测试
--解释--
当您将成员函数指定为virtual时,您可能会尝试在运行时通过基类以多态方式使用子类。为了保持c++对语言设计的性能保证,他们提供了尽可能轻的实现策略--即,一级间接,并且仅当一个类可能在运行时被多态使用时,程序员通过将至少一个函数设置为虚拟函数来指定这一点。
如果避免使用virtual关键字,则不会产生vtable的开销。
-- edit :反映您的编辑--
只有当基类包含虚函数时,任何其他子类才包含vtable。所述基类的父类没有vtable。
在您的示例中,所有三个类都将有一个vtable,这是因为您可以尝试通过A*.使用所有三个类
测试-- GCC 4+ --
#include <iostream>
class test_base
{
public:
void x(){std::cout << "test_base" << "\n"; };
};
class test_sub : public test_base
{
public:
virtual void x(){std::cout << "test_sub" << "\n"; } ;
};
class test_subby : public test_sub
{
public:
void x() { std::cout << "test_subby" << "\n"; }
};
int main()
{
test_sub sub;
test_base base;
test_subby subby;
test_sub * psub;
test_base *pbase;
test_subby * psubby;
pbase = ⊂
pbase->x();
psub = &subby;
psub->x();
return 0;
}输出
test_base
test_subbytest_base没有虚拟表,因此任何向其强制转换的内容都将使用来自test_base的x()。另一方面,test_sub改变了x()的性质,它的指针将间接通过vtable,这从执行test_subby的x()中可以看出。
因此,只有在使用关键字virtual时,才会在层次结构中引入vtable。较老的祖先没有vtable,如果发生向下转换,它将被硬连接到祖先函数。
https://stackoverflow.com/questions/1963926
复制相似问题