C++中,在对象的虚函数表中,无法通过虚函数地址调用虚析构函数?

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;
}

请问这是为什么呢,为什么会找不到虚析构函数呢?

阅读 1.7k
1 个回答

你这个程序已经不能按照 C++ 来解释了。从 C++ 的观点来看,里面充斥了未定义行为,完全就是一个错误的程序。

下面从编译器的具体实现的角度,看看有哪些可能的问题。由于是看编译器的具体实现,那么不同的编译器的实现可能就是不同的。所以下面也就是一个大概思路。

虚表的位置和组织方式在不同的编译器中会有不同。具体现在所使用编译器是怎么组织的,可以看一眼实际虚函数调用的汇编代码,大概就可以猜到。然后可以根据汇编在写程序模拟。

函数是有不同的“调用协议”的。(这个不是 C++ 规定的,完全是实现细节,同样不同的编译器、操作系统(windos, linux)、体系结构(x86,arm)都会不同。)所谓调用协议,大概就是参数放在什么地方(寄存器、堆栈),如果放堆栈里那么是怎么排列的(从左向右入栈还是从右向左),调用结束之后是由被调函数还是主调函数清理堆栈等等。类成员函数由于比普通函数多了一个 “this” 参数,所以通常调用协议与普通函数是不一样的。你程序里定义的 func 是一个普通函数,用它来调用成员函数肯定是不行的,因为调用协议不一样。


我觉得要了解虚表,不如写一个实际用到虚函数的程序,然后看看实际生成的汇编是什么样子的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题