首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么vptr不是静态的?

为什么vptr不是静态的?
EN

Stack Overflow用户
提问于 2012-12-17 20:26:50
回答 7查看 3.3K关注 0票数 14

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

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2012-12-17 20:28:39

对象的运行时类是对象本身的属性。实际上,vptr代表运行时类,因此不能为static。但是,它所指向的内容可以由同一运行时类的所有实例共享。

票数 9
EN

Stack Overflow用户

发布于 2012-12-17 22:10:54

你的图表是错的。没有一个单独的vtable,每个多态类型都有一个vtable。A的vptr指向A的vtable,A1的vptr指向A1的vtable,依此类推。

给定:

代码语言:javascript
复制
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中的第一个函数。

假设一个编译器使用了你的想法,我们写道:

代码语言:javascript
复制
A1 a1;
A2 a2;
A& a = (std::rand() % 2) ? a1 : a2;
a.foo();

编译器查看基类A并找到类A的vptr,根据您的想法,该类是A类型的static属性,而不是引用a绑定到的对象的成员。该vptr是否指向AA1A2或其他内容的vtable?如果它指向A1的vtable,那么当a引用a2时,50%的时间都是错误的,反之亦然。

现在假设我们这样写:

代码语言:javascript
复制
A1 a1;
A2 a2;
A& a = a1;
A& aa = a2;
a.foo();
aa.foo();

aaa都是对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直接存储在对象中。

对于a1a2,vptr必须不同,并且在通过指针或对基类的引用访问它们时,必须可以在不知道动态类型的情况下访问vptr,这样当您通过对基类a的引用获得vptr时,它仍然指向右侧的vtable,而不是基类vtable。最明显的方法是将vptr直接存储在对象中。一种替代的、更复杂的解决方案将是保持对象地址到vptr的映射,例如类似std::map<void*, vtable*>的映射,并且通过查找&a来找到用于a的vtable,但是这仍然为每个对象存储一个vptr,而不是每个类型存储一个vptr,并且将需要更多的工作(和动态分配)来在每次创建和销毁多态对象时更新映射,并且将增加总体存储器使用,因为映射结构将占用空间。将vptr嵌入到对象本身中会更简单。

票数 5
EN

Stack Overflow用户

发布于 2012-12-17 20:35:21

虚拟表(顺便说一句,这是C++标准中没有提到的一种实现机制)用于在运行时标识对象的动态类型。因此,对象本身必须持有指向它的指针。如果它是静态的,那么它只能识别静态类型,并且它将是无用的。

如果您正在考虑以某种方式在内部使用typeid()来标识动态类型,然后使用它调用静态指针,请注意typeid()只返回属于具有虚函数的类型的对象的动态类型;否则它只返回静态类型(在当前的C++标准中为§5.2.8 )。是的,这意味着它以相反的方式工作:typeid()通常使用虚拟指针来标识动态类型。

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

https://stackoverflow.com/questions/13914178

复制
相关文章

相似问题

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