每个包含一个或多个虚函数的类都有一个关联的Vtable。称为vptr的空指针指向该vtable。该类的每个对象都包含指向同一个Vtable的vptr。那么为什么vptr不是静态的呢?与其将vptr与对象关联,为什么不将其与类关联呢?

发布于 2012-12-17 20:28:39
对象的运行时类是对象本身的属性。实际上,vptr代表运行时类,因此不能为static。但是,它所指向的内容可以由同一运行时类的所有实例共享。
发布于 2012-12-17 22:10:54
你的图表是错的。没有一个单独的vtable,每个多态类型都有一个vtable。A的vptr指向A的vtable,A1的vptr指向A1的vtable,依此类推。
给定:
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();
};A的vtable包含{ &A::foo, &A::bar }
A1的vtable包含{ &A1::foo, &A::bar }
A2的vtable包含{ &A2::foo, &A::bar }
A3的vtable包含{ &A::foo, &A3::bar, &A3::baz }
因此,当您调用a.foo()时,编译器跟随对象的vptr查找vtable,然后调用vtable中的第一个函数。
假设一个编译器使用了你的想法,我们写道:
A1 a1;
A2 a2;
A& a = (std::rand() % 2) ? a1 : a2;
a.foo();编译器查看基类A并找到类A的vptr,根据您的想法,该类是A类型的static属性,而不是引用a绑定到的对象的成员。该vptr是否指向A、A1、A2或其他内容的vtable?如果它指向A1的vtable,那么当a引用a2时,50%的时间都是错误的,反之亦然。
现在假设我们这样写:
A1 a1;
A2 a2;
A& a = a1;
A& aa = a2;
a.foo();
aa.foo();a和aa都是对A的引用,但它们需要两个不同的vptr,一个指向A1的vtable,另一个指向A2的vtable。如果vptr是A的静态成员,它怎么能同时有两个值呢?唯一符合逻辑且一致的选择是,A的静态vptr指向A的vtable。
但这意味着call a.foo()在应该调用A1::foo()时调用A::foo(),而call aa.foo()在应该调用A2::foo()时也调用A2::foo()。
显然,您的想法未能实现所需的语义,从而证明使用您的想法的编译器不可能是C++编译器。在不知道派生类型是什么的情况下,编译器无法从a获取A1的vtable (这通常是不可能的,对基的引用可以从定义在不同库中的函数返回,并且可以引用甚至还没有编写的派生类型!)或者通过将vptr直接存储在对象中。
对于a1和a2,vptr必须不同,并且在通过指针或对基类的引用访问它们时,必须可以在不知道动态类型的情况下访问vptr,这样当您通过对基类a的引用获得vptr时,它仍然指向右侧的vtable,而不是基类vtable。最明显的方法是将vptr直接存储在对象中。一种替代的、更复杂的解决方案将是保持对象地址到vptr的映射,例如类似std::map<void*, vtable*>的映射,并且通过查找&a来找到用于a的vtable,但是这仍然为每个对象存储一个vptr,而不是每个类型存储一个vptr,并且将需要更多的工作(和动态分配)来在每次创建和销毁多态对象时更新映射,并且将增加总体存储器使用,因为映射结构将占用空间。将vptr嵌入到对象本身中会更简单。
发布于 2012-12-17 20:35:21
虚拟表(顺便说一句,这是C++标准中没有提到的一种实现机制)用于在运行时标识对象的动态类型。因此,对象本身必须持有指向它的指针。如果它是静态的,那么它只能识别静态类型,并且它将是无用的。
如果您正在考虑以某种方式在内部使用typeid()来标识动态类型,然后使用它调用静态指针,请注意typeid()只返回属于具有虚函数的类型的对象的动态类型;否则它只返回静态类型(在当前的C++标准中为§5.2.8 )。是的,这意味着它以相反的方式工作:typeid()通常使用虚拟指针来标识动态类型。
https://stackoverflow.com/questions/13914178
复制相似问题