0

我在哪里可以找到 C++ 有状态虚拟基础的一个很好的解释?

我查看了JSF C++ 编码标准并阅读了他们的解释,但正在寻找一些额外的信息。

感谢您提供的任何其他详细信息。

4

2 回答 2

3

Stanley Lippman 的书“Inside the C++ Object Model”很好地贯穿了这个主题(虽然过时了,但仍然有效)。

于 2012-05-30T15:56:04.207 回答
2

虚拟性是关于间接性的。让我们从简单的开始:

struct Foo { void bar(int, bool) {} } x;
x.bar(12, false);

这里对Foo::bar()实例的调用x在编译时是完全已知的并静态解析:一个固定函数,它被赋予实例引用和函数参数。函数名,调用,完成。到目前为止没有问题。

继续:

struct Boo { virtual void bar(char, float) = 0; };
extern Boo * foreign_function();

Boo * p = foreign_function();
p->bar('a', -1.5);

这一次,在编译时无法知道bar()调用应该去哪里。解决此问题的唯一方法是添加一个间接级别,允许您查找此成员函数的所有可能覆盖并在运行时选择正确的覆盖,具体取决于*p. 这次我们从函数名开始,在运行时执行查找,然后进行调用。这种模式应该仍然相当熟悉。

这里的要点是,知道动态类型*p是(非虚拟)基础的子类型就足够了,Boo这样我们只需一次查找就可以实现这一点(例如,指向与表兼容的表的 vtable 指针)Boo)。

现在说大鱼:

struct Voo { virtual void doo(double, void *) = 0; };
struct Left  : virtual Voo { virtual void doo(double, void *); } };
struct Right : virtual Voo { virtual void doo(double, void *); } };
struct Most : Left, Right  { virtual void doo(double, void *); } };

Left * p = /* address of a Most object, say */;
p->doo(0.1, nullptr);

我们已经知道我们不知道doo()应该去哪里,我们必须在运行时查找它。但是,不再可能进行简单的一步间接寻址。即使Left是 的子类,Voo也是Right的子类Voo,但实际的Voo基本子对象*p实际上并不是Left- 或 -子对象的子对象Right- (唯一!)虚拟子对象直接属于Most(或任何最派生的对象是)。在实现方面,单个 vtable 指针是不好的,因为我们不需要Left' 的 vpointer,也不需要Right' 的 vptr。相反,我们想要实际对象具有的任何 vpointer。

所以现在我们发现自己处于一个熟悉的情况:我们需要查找一些我们只能在运行时知道的东西。而这次我们需要查找的是实际的虚拟基地。所以过程是这样的:函数名,在运行时查找虚基,在虚基中查找虚函数,然后进行调用。(在虚拟的典型 vtable 实现中,这通常通过“thunk”或“指向指针的指针”进行额外的查找来完成。)

简而言之,“虚拟”意味着“在运行时确定”。

(这并不强制您的编译器生成运行时代码。如果在编译时可以证明调度的目标是已知的,则调用可能会被去虚拟化。但是您的程序的行为是“好像”。)

于 2012-05-30T17:20:09.290 回答