C++支持通过虚拟机制进行动态绑定.但据我所知,虚拟机制是编译器的实现细节,标准只是指定了在特定场景下应该发生的行为.大多数编译器通过虚拟表和虚拟指针实现虚拟机制.是的,我知道这是如何工作的,所以我的问题不是关于虚拟指针和表的实现细节.我的问题是:
sizeof只有一个虚函数的任何类的将是一个指针(vptr的内部尺寸this)上编译,所以考虑到虚拟PTR和TBL机制本身是编译器实现,将这个说法我在上面做永远是真的吗?首先,我想清楚地表明我确实理解在C++标准中没有vtable和vptrs的概念.但是我认为几乎所有实现都以几乎相同的方式实现虚拟调度机制(如果我错了,请纠正我,但这不是主要问题).另外,我相信我知道虚函数是如何工作的,也就是说,我总能告诉我将调用哪个函数,我只需要实现细节.
假设有人问我以下内容:
"您的基类B具有虚函数v1,v2,v3和派生类D:B,它会覆盖函数v1和v3并添加虚函数v4.解释虚拟调度的工作原理".
我会这样回答:
对于每个具有虚函数的类(在本例中为B和D),我们有一个单独的指向函数的数组,称为vtable.
B的vtable将包含
&B::v1
&B::v2
&B::v3
Run Code Online (Sandbox Code Playgroud)
D的vtable将包含
&D::v1
&B::v2
&D::v3
&D::v4
Run Code Online (Sandbox Code Playgroud)
现在B类包含一个成员指针vptr.D自然地继承它,因此也包含它.在BB的构造函数和析构函数中设置vptr指向B的vtable.在DD的构造函数和析构函数中,它指向D的vtable.
对多态类X的对象x上的虚函数f的任何调用都被解释为对x.vptr的调用[f在vtables中的位置]
问题是:
1.我在上述描述中有任何错误吗?
2.编译器如何知道f在vtable中的位置(请详细说明)
3.这是否意味着如果一个类有两个基数那么它有两个vpt?在这种情况下发生了什么?(尝试以与我相似的方式描述,尽可能详细地描述)
4.钻石层次结构中发生了什么,其中A位于顶部B,C位于中间,D位于底部?(A是B和C的虚拟基类)
提前致谢.
我试图理解书中有效的c ++语句.以下是多继承的继承图.


现在这本书说vptr需要每个类中的单独内存.它也做了以下声明
上图中的一个奇怪之处在于,即使涉及四个类,也只有三个vptrs.如果愿意,实现可以自由地生成四个vpt,但是三个就足够了(事实证明B和D可以共享一个vptr),并且大多数实现利用这个机会来减少编译器生成的开销.
我看不出有什么理由为什么每个类都要求为vptr提供单独的内存.我理解vptr是从基类继承的,可能是继承类型.如果我们假设它显示了带有继承的vptr的结果内存结构,那么它们如何才能生成该语句
B和D可以共享vptr
有人可以在多重继承中澄清一下vptr吗?
我最近发布了一个关于由于C++中的虚拟性导致的内存开销的问题.答案让我了解vtable和vptr的工作原理.我的问题如下:我在超级计算机上工作,我有数十亿个对象,因此我不得不关心由于虚拟性造成的内存开销.经过一些测量,当我使用具有虚函数的类时,每个派生对象都有其8字节的vptr.这根本不可忽视.
我想知道intel icpc或g ++是否有一些配置/选项/参数,使用"全局"vtable和可调精度的索引而不是vptr.因为这样的事情将允许我使用2字节索引(unsigned short int)而不是8字节vptr用于数十亿个对象(并且很好地减少了内存开销).有没有办法用编译选项做到这一点(或类似的东西)?
非常感谢你.
在Java中:
class Base {
public Base() { System.out.println("Base::Base()"); virt(); }
void virt() { System.out.println("Base::virt()"); }
}
class Derived extends Base {
public Derived() { System.out.println("Derived::Derived()"); virt(); }
void virt() { System.out.println("Derived::virt()"); }
}
public class Main {
public static void main(String[] args) {
new Derived();
}
}
Run Code Online (Sandbox Code Playgroud)
这将输出
Base::Base()
Derived::virt()
Derived::Derived()
Derived::virt()
Run Code Online (Sandbox Code Playgroud)
但是,在C++中,结果是不同的:
Base::Base()
Base::virt() // ? Not Derived::virt()
Derived::Derived()
Derived::virt()
Run Code Online (Sandbox Code Playgroud)
(有关C++代码,请参阅http://www.parashift.com/c++-faq-lite/calling-virtuals-from-ctors.html)
什么导致Java和C++之间的这种差异?这是vtable初始化的时间吗?
编辑:我确实理解Java和C++机制.我想知道的是这个设计决定背后的见解.
包含一个或多个虚函数的每个类都有一个与之关联的Vtable.一个名为vptr的void指针指向该vtable.该类的每个对象都包含指向同一Vtable的vptr.那为什么vptr不是静态的呢?而不是将vptr与对象相关联,为什么不将它与类关联?

我对vptr和内存中对象的表示感到困惑,希望你能帮助我更好地理解这个问题.
考虑B从中继承A并定义虚函数f().从我了解到的记忆B类对象的表示是这样的:[ vptr | A | B ]
与vtbl该vptr指向包含B::f().我也明白,从铸造对象B来A什么都不做,除了忽略B在对象的端部.这是真的吗?这种行为不对吗?我们希望类型的对象A执行A::f()方法而不是B::f().
是否有一些vtables在系统中的类的数量?
一个将如何vtable类,由两个或多个类继承的是什么样子?如何将C的对象表示在内存中?
与问题3相同,但具有虚拟继承.
我正在看这篇文章,它说"在进入基类析构函数后,该对象成为基类对象,而C++的所有部分 - 虚函数,dynamic_casts等 - 以这种方式对待它." 这是否意味着vptr在破坏期间发生了变化?这是怎么发生的?
假设我们有以下程序:
class A
{ public:
virtual fun(){};
};
class B:public A
{ public:
virtual fun(){};
};
int main()
{
A a1;
B b1;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,当我们运行这个程序时,会创建多少vtables和多少vptrs?
我对vtable的理解是,如果我有一个带有虚函数的Cat,带有子类Lion和HouseCat的函数,那么有一个vtable,它将speak()映射到每个子类的正确实现.所以一个电话
cat.speak()
Run Code Online (Sandbox Code Playgroud)
编译成
cat.vtable[0]()
Run Code Online (Sandbox Code Playgroud)
也就是说,在vtable位置0中查找并在该位置调用函数指针.
我的问题是:多重继承会发生什么?
让我们添加一个Pet类.Pet有虚函数speak()和eat().HouseCat扩展了Pet,而Lion则没有.现在,我需要确保这一点
pet.eat()
Run Code Online (Sandbox Code Playgroud)
编译为
pet.vtable[1]()
Run Code Online (Sandbox Code Playgroud)
那就是vtable [0]需要说话().Pet.eat需要是插槽1.这是因为cat.speak()需要访问vtable中的插槽0,如果对于HouseCat,插槽0恰好吃了,这将是非常错误的.
编译器如何确保vtable索引适合在一起?
c++ ×10
vptr ×10
vtable ×7
virtual ×2
compilation ×1
destruction ×1
function ×1
g++ ×1
intel ×1
java ×1