C++ 为什么vptr不是静态的?
每个包含一个或多个虚拟函数的类都有一个与其关联的Vtable。一个名为vptr的空指针指向该vtable。该类的每个对象都包含指向同一Vtable的vptr。那么为什么vptr不是静态的呢?与其将vptr与对象关联,为什么不将其与类关联C++ 为什么vptr不是静态的?,c++,function,virtual,vtable,vptr,C++,Function,Virtual,Vtable,Vptr,每个包含一个或多个虚拟函数的类都有一个与其关联的Vtable。一个名为vptr的空指针指向该vtable。该类的每个对象都包含指向同一Vtable的vptr。那么为什么vptr不是静态的呢?与其将vptr与对象关联,为什么不将其与类关联 对象的运行时类是对象本身的属性。实际上,vptr表示运行时类,因此不能是静态的。但是,它所指向的内容可以被同一运行时类的所有实例共享。vptr的整个要点是,您不知道对象在运行时具有哪个类。如果您知道这一点,那么虚拟函数调用将是不必要的。事实上,这就是不使用虚拟函
对象的运行时类是对象本身的属性。实际上,
vptr
表示运行时类,因此不能是静态的
。但是,它所指向的内容可以被同一运行时类的所有实例共享。vptr的整个要点是,您不知道对象在运行时具有哪个类。如果您知道这一点,那么虚拟函数调用将是不必要的。事实上,这就是不使用虚拟函数时会发生的情况。但如果我有虚拟函数的话
class Sub : Parent {};
还有一个类型为
Parent*
的值,我不知道在运行时这是Parent
类型的对象还是Sub
类型的对象。vptr让我明白了这一点。虚拟方法表是每个类的。对象包含指向运行时类型vptr的指针
我不认为这是标准半身像中的一个要求,我使用过的所有编译都是这样做的
即使在您的示例中也是如此。
< P>虚拟表(顺便说一下,在C++标准中没有提到的实现机制)用于在运行时标识对象的动态类型。因此,对象本身必须持有指向它的指针。如果它是静态的,那么它只能识别静态类型,这将是无用的如果您想在内部使用
typeid()
来标识动态类型,然后用它调用静态指针,请注意typeid()
只返回属于具有虚拟函数的类型的对象的动态类型;否则,它只返回静态类型(当前的C++标准中的5.2.8)。是的,这意味着它的工作方式正好相反:typeid()
通常使用虚拟指针来标识动态类型。您的图表是错误的。没有单个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();
A1 a1;
A2 a2;
A& a = a1;
A& aa = a2;
a.foo();
aa.foo();
编译器在基类A
中查找并找到类A
的vptr,该类(根据您的想法)是A
类型的静态属性,而不是引用A
绑定到的对象的成员。该vptr是否指向A
、或A1
或A2
或其他内容的vtable?如果它指向A1
的vtable,当a
引用a2
时,50%的时间是错误的,反之亦然
现在假设我们写:
A1 a1;
A2 a2;
A& a = (std::rand() % 2) ? a1 : a2;
a.foo();
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
但这意味着调用a.foo()
在应该调用A1::foo()
时调用a::foo()
,调用aa.foo()
在应该调用A2::foo()
时也调用a::foo()
显然,您的想法无法实现所需的语义,证明使用您的想法的编译器不能是C++编译器。编译器无法在不知道派生类型是什么的情况下从a
获取A1
的vtable(这通常是不可能的,对base的引用可能是从其他库中定义的函数返回的,并且可能引用尚未编写的派生类型!)或者将vptr直接存储在对象中
对于a1
和a2
,vptr必须不同,并且在通过poiner或对base的引用访问它们时,必须在不知道动态类型的情况下进行访问,这样,当您通过对基类a
的引用获得vptr时,它仍然指向右侧的vtable,而不是基类vtable。最明显的方法是将vptr直接存储在对象中。另一种更复杂的解决方案是保留对象地址到vptr的映射,例如类似于std::map
,并通过查找&a
找到a
的vtable,但这仍然会为每个对象存储一个vptr,而不是每种类型一个,并且需要更多的工作(和动态分配)在每次创建和销毁多态对象时更新映射,会增加总体内存使用,因为映射结构会占用空间。只需将vptr嵌入对象本身就更简单。因为每个人都证明vptr是对象的属性。
让我们看看为什么
假设我们有三个物体
类基{
虚拟~Base();
//类定义
};
派生类:公共基{
//类定义
};
类客户端:公共De