首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么时候在C++中创建vtable?

什么时候在C++中创建vtable?
EN

Stack Overflow用户
提问于 2009-12-27 02:02:02
回答 6查看 20.7K关注 0票数 27

编译器究竟在什么时候创建虚拟函数表?

1)当类至少包含一个虚函数时。

2)当直接基类包含至少一个虚函数时。

3)当层次结构的任何级别的任何父类包含至少一个虚拟函数时。

与此相关的一个问题是:在C++层次结构中放弃动态调度是可能的吗?

例如,考虑下面的例子。

代码语言:javascript
复制
#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类是否获得动态多态性?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 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。

你一般不应该担心这样的细节。重要的是你是否有虚拟分派。通过显式指定要调用的函数,你不必使用虚拟分派,但这通常只在实现虚拟方法时有用(比如调用基的方法)。示例:

代码语言:javascript
复制
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指针。
  • 如果一个类声明了非继承的虚方法(例如当它没有基类时),那么它必须有它自己的vtable。
  • 如果一个类有一组不同于它的第一个基类的重写方法,那么它必须有它自己的vtable,并且不能重用基类的vtable。(析构函数通常需要这个。)
  • 如果一个类有多个基类,对于具有虚方法的第二个或更晚的基类,如果没有较早的基类具有虚方法,并且对所有较早的基类应用了空基优化,则将此基类视为第一个基类class.
  • Otherwise,类必须具有其自己的vtable.

  • 如果一个类有任何虚拟基类,它必须有自己的vtable。

请记住,vtable类似于类的静态数据成员,实例只有指向它们的指针。

还可以参阅Jan Gray.撰写的综合文章 (1994年3月)(如果该链接失效,请单击Try Google。)

重用vtable的示例:

代码语言:javascript
复制
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。

票数 24
EN

Stack Overflow用户

发布于 2009-12-27 02:08:46

答案是,“视情况而定”。这取决于你所说的“包含一个vtbl”是什么意思,也取决于特定编译器的实现者所做的决定。

严格地说,没有“类”包含虚函数表。某些类的某些实例包含指向虚拟函数表的指针。然而,这只是语义的一种可能的实现。

在极端情况下,编译器可以假设将一个唯一的数字放入实例中,该实例被索引到用于选择适当的虚拟函数实例的数据结构中。

如果你问,‘GCC是做什么的?’或者“可视化C++是做什么的?”然后你就可以得到一个具体的答案。

@Hassan Syed的答案可能更接近你所问的问题,但在这里保持概念的直截了当是非常重要的。

有行为(基于新类的动态调度)和实现。你的问题使用了实现术语,尽管我怀疑你是在寻找行为的答案。

行为上的答案是这样的:任何声明或继承虚拟函数的类都将在调用该函数时表现出动态行为。任何不这样做的类都不会。

在实现方面,编译器被允许做任何它想要实现的结果。

票数 8
EN

Stack Overflow用户

发布于 2009-12-27 02:11:29

应答

当类声明包含虚函数时,将创建vtable。当父级--在父级结构中的任何地方--有一个虚拟函数时,就会引入vtable,让我们称这个父级为Y。Y的任何父级都不会有vtable (除非他们的父级结构中有某个其他函数的virtual )。

继续阅读以进行讨论和测试

--解释--

当您将成员函数指定为virtual时,您可能会尝试在运行时通过基类以多态方式使用子类。为了保持c++对语言设计的性能保证,他们提供了尽可能轻的实现策略--即,一级间接,并且仅当一个类可能在运行时被多态使用时,程序员通过将至少一个函数设置为虚拟函数来指定这一点。

如果避免使用virtual关键字,则不会产生vtable的开销。

-- edit :反映您的编辑--

只有当基类包含虚函数时,任何其他子类才包含vtable。所述基类的父类没有vtable。

在您的示例中,所有三个类都将有一个vtable,这是因为您可以尝试通过A*.使用所有三个类

测试-- GCC 4+ --

代码语言:javascript
复制
#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 = &sub;
  pbase->x();
  psub = &subby;
  psub->x();

  return 0;
}

输出

代码语言:javascript
复制
test_base
test_subby

test_base没有虚拟表,因此任何向其强制转换的内容都将使用来自test_basex()。另一方面,test_sub改变了x()的性质,它的指针将间接通过vtable,这从执行test_subbyx()中可以看出。

因此,只有在使用关键字virtual时,才会在层次结构中引入vtable。较老的祖先没有vtable,如果发生向下转换,它将被硬连接到祖先函数。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1963926

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档