对象的运行时类是对象本身的属性.实际上,vptr
表示运行时类,因此不能static
.但是,它指向的内容可以由同一运行时类的所有实例共享.
你的图有误。没有一个 vtable,每个多态类型都有一个 vtable。vptr forA
指向 vtable for A
, vptr forA1
指向 vtable forA1
等。
鉴于:
class A {
public:
virtual void foo();
virtual void bar();
};
class A1 : public A {
virtual void foo();
};
class A2 : public A {
virtual void foo();
};
class A3 : public A {
virtual void bar();
virtual void baz();
};
Run Code Online (Sandbox Code Playgroud)
对于虚函数表A
包含{ &A::foo, &A::bar }
了V表A1
中包含{ &A1::foo, &A::bar }
的虚函数表的A2
包含{ &A2::foo, &A::bar }
的虚函数表的A3
包含{ &A::foo, &A3::bar, &A3::baz }
所以当你调用a.foo()
编译器时会跟随对象的 vptr 来查找 vtable 然后调用 vtable 中的第一个函数。
假设一个编译器使用你的想法,我们写:
A1 a1;
A2 a2;
A& a = (std::rand() % 2) ? a1 : a2;
a.foo();
Run Code Online (Sandbox Code Playgroud)
编译器在基类中A
查找并找到该类的 vptr,该类A
(根据您的想法)是static
该类型的属性,而A
不是引用a
绑定到的对象的成员。该 vptr 是否指向 vtable for A
, or A1
or A2
or 或其他东西?如果它指向 vtable 因为A1
它在a
引用时有 50% 的时间是错误的a2
,反之亦然。
现在假设我们写:
A1 a1;
A2 a2;
A& a = a1;
A& aa = a2;
a.foo();
aa.foo();
Run Code Online (Sandbox Code Playgroud)
a
和aa
都是对 的引用A
,但它们需要两个不同的 vptr,一个指向 vtable for A1
,一个指向 vtable for A2
。如果 vptr 是一个静态成员,A
它如何一次具有两个值?唯一合乎逻辑的、一致的选择是静态 vptr ofA
指向 vtable for A
。
但这意味着a.foo()
调用A::foo()
在它应该调用的时候调用A1::foo()
,并且调用aa.foo()
也在A::foo()
它应该调用的时候调用A2::foo()
。
显然,您的想法未能实现所需的语义,证明使用您的想法的编译器不能是 C++ 编译器。有没有办法让编译器来获取虚函数表的A1
从a
没有任何知道什么是派生类型是(这是一般不可能,参考-底部可能已经从不同的库中定义的函数返回,可能指尚未编写的派生类型!)或将 vptr 直接存储在对象中。
vptr 必须a1
与a2
and不同,并且必须在不知道动态类型的情况下通过指针或对基类的引用访问它们时可以访问,这样当您通过对基类的引用获得 vptr 时a
,它仍然指向右侧vtable,而不是基类 vtable。最明显的方法是将 vptr 直接存储在对象中。另一种更复杂的解决方案是将对象地址映射到 vptrs,例如类似的东西std::map<void*, vtable*>
,并a
通过查找找到 vtable&a
,但这仍然为每个对象存储一个 vptr,而不是每个类型一个,并且每次创建和销毁多态对象时都需要更多的工作(和动态分配)来更新映射,并且会增加整体内存使用量,因为映射结构会占用空间。将 vptr 嵌入对象本身更简单。
归档时间: |
|
查看次数: |
2682 次 |
最近记录: |