C++中,对于包含虚函数的类,编译器都会为其分配至少一个虚函数表,里面存储着虚函数的地址。那么按道理,可以通过在虚函数表中取得虚函数的地址,来调用对象的虚函数。
但是我只能在虚函数表中找到非虚析构函数的地址,并且可以通过函数指针正常调用,而且虚函数表的大小是非虚析构函数的个数。
这段代码可以正常运行
#include <iostream>
using namespace std;
// 定义一个指向虚析构函数的指针类型
typedef void (*func)(void);
// 基类
class Base {
public:
// 虚析构函数
virtual void print() {
cout << "Base print" << endl;
}
};
// 派生类
class Derived : public Base {
public:
// 虚析构函数
virtual ~Derived() {
cout << "Derived destructor" << endl;
}
};
int main() {
// 创建一个派生类对象
Derived obj;
typedef void (*func)(void);
int ** vptb = (int **)(&obj);
func fn = func((int *)vptb[0][0]);
fn();
return 0;
}
而下面的代码,运行会出现非法内存访问的错误
#include <iostream>
using namespace std;
// 定义一个指向虚析构函数的指针类型
typedef void (*func)(void);
// 基类
class Base {
public:
// 虚析构函数
virtual ~Base() {
cout << "Base destructor" << endl;
}
};
// 派生类
class Derived : public Base {
public:
// 虚析构函数
virtual ~Derived() {
cout << "Derived destructor" << endl;
}
};
int main() {
// 创建一个派生类对象
Derived obj;
typedef void (*func)(void);
int ** vptb = (int **)(&obj);
func fn = func((int *)vptb[0][0]);
fn();
return 0;
}
请问这是为什么呢,为什么会找不到虚析构函数呢?
你这个程序已经不能按照 C++ 来解释了。从 C++ 的观点来看,里面充斥了未定义行为,完全就是一个错误的程序。
下面从编译器的具体实现的角度,看看有哪些可能的问题。由于是看编译器的具体实现,那么不同的编译器的实现可能就是不同的。所以下面也就是一个大概思路。
虚表的位置和组织方式在不同的编译器中会有不同。具体现在所使用编译器是怎么组织的,可以看一眼实际虚函数调用的汇编代码,大概就可以猜到。然后可以根据汇编在写程序模拟。
函数是有不同的“调用协议”的。(这个不是 C++ 规定的,完全是实现细节,同样不同的编译器、操作系统(windos, linux)、体系结构(x86,arm)都会不同。)所谓调用协议,大概就是参数放在什么地方(寄存器、堆栈),如果放堆栈里那么是怎么排列的(从左向右入栈还是从右向左),调用结束之后是由被调函数还是主调函数清理堆栈等等。类成员函数由于比普通函数多了一个 “this” 参数,所以通常调用协议与普通函数是不一样的。你程序里定义的
func
是一个普通函数,用它来调用成员函数肯定是不行的,因为调用协议不一样。我觉得要了解虚表,不如写一个实际用到虚函数的程序,然后看看实际生成的汇编是什么样子的。