虚表的顺序重要吗?
Is the order of the virtual table important?
我是新来的,所以别对我太苛求。根据我的讲师前段时间所说的,虚拟桌子的顺序很重要。但我不明白为什么!!?
给定下一个代码:
class A
{
public:
A() {cout <<"1" << endl;};
A (const A& s) {cout << "2" << endl;}
~A () {cout << "3" << endl;}
void f1() {cout << "4" << endl; f2();}
virtual void f2() = 0;
virtual void f3() {cout << "5" << endl;}
};
class B : public A
{
public:
B() {cout << "6" << endl;}
B(const B& b) : A(b) {cout << "7" << endl;}
~B() {cout << "8" << endl;}
virtual void f1() {cout<<"9"<<endl;}
void f2() {cout<<"lO"<<endl; f4();}
virtual void f2(int i) {cout << "11" << endl;}
virtual void f4() {cout << "12" << endl; f3();}
};
他说顺序是:
A's vtable :
A::f2()
A::f3()
B's vtable :
B::f2()
A::f3()
B::f1()
B::f2(int)
B::f4()
但是我不明白为什么它很重要?他说桌子是没用的,如果它不是按照正确的顺序,你能解释一下原因吗?
在c++标准中没有变量的概念。只是大多数实现(如果不是全部的话)将它用于虚拟分派。然而,确切的约定完全是由实现定义的。
说……函数的顺序很重要,但不是对程序员来说,而是对编译器来说——你可以在代码中随心所欲地安排函数。然而,编译器通常会将每个函数指针放入虚函数表中的特定位置,并将其专用于该函数。因此,当它需要调用f()
时,它知道f()
函数的索引,并从虚函数表中获取该指针。
这个问题可能对您也有帮助:虚拟调度实现细节
虚变量表的顺序对事情的正常工作很重要,但只对编译器(也就是说,你不需要关心,因为它会照顾它)。
如果编译器自己把它弄乱了,那么是的,事情会破裂,因为函数是通过偏移量查找的(所以偏移量会产生一个随机的函数,这将是灾难性的)。但是一般程序员不需要担心虚函数表的顺序
只有当类为外部ABI声明接口(例如COM/XPCOM)时才重要。
大多数时候它并不重要,没有理由去关心它
虚表的每个客户端都需要知道正确的顺序,以便找到要调用的正确方法。但只要各方都同意这个顺序,这个顺序是什么都无所谓。
虚值表是一个"查找"表。它基本上是指向类虚函数的指针的映射。如果顺序不对,指针将指向错误的函数。例如,如果您想调用不带参数的B:f1()
,而要调用带int
参数的B::f2()
,则会发生不好的事情。
我不确定他是什么意思,但我会试着解释它是如何工作的:
首先,c++通过名称和签名来定义方法。因此,当c++初始化一个类的虚表时,它将用具有相同名称和签名的派生虚函数替换所有基类的虚函数。
当一个类派生另一个类时,它实际上是在它之上构建的。因此基类作为内存块的一部分存在(复杂,请阅读这里- 虚拟继承)
虚拟表只保存一个"指针"到正确的函数
在visual studio中,当您更改虚函数的声明顺序时,您可能需要在之后清理并重新构建整个解决方案。
我的猜测是虚表在特定情况下不同步。
因此,如果在更改虚拟函数后事情变得不稳定,只需重新编译即可解决问题。
- 如何按数字顺序插入链表节点?
- 虚表中虚函数的地址
- 通过虚表接口使用Excel
- 在静态分配对象上调用虚函数时使用的虚表
- 为什么在析构函数中将虚表设置回该级别
- 编译一个Qt单文件从命令行:未定义的引用到虚表
- Qt与代码块-未定义的引用虚表
- 在多重继承中,被重写的虚方法保存在c++的虚表中
- 对虚表的未定义引用.Q_OBJECT宏
- 在c++中禁用虚函数的动态绑定(创建虚表)
- 尽管没有虚函数,对虚表的未定义引用
- c++虚表查询
- 虚表的顺序重要吗?
- 对虚表、构造函数和析构函数的未定义引用
- 我试图访问一个c++多态类的虚表,但失败的核心转储,为什么
- 对虚表的未定义引用
- 不带虚表的c++动态分派
- 为什么在虚表中需要一个纯虚函数表项
- 未定义对类的虚表的引用
- 虚表/调度表