C++虚表的有关问题

C++虚表的问题
下午研究了一下虚表的机制,在CodeBlocks上试验了一下,有两个问题,如下

#include <iostream>
using namespace std;
class Base1{
    int iBase1;
    public:
        Base1(int i=0):iBase1(i){}
        virtual ~Base1(){
            cout << "Base1 destoried\n";
        }
        virtual void print(){
            cout << "Base1::print\n";
        }
        virtual void f(){
            cout << "Base1::f\n";
        }
};
class Base2{
    int iBase2;
    public:
        Base2(int i=1):iBase2(i){}
        virtual ~Base2(){
            cout << "Base2 destoried\n";
        }
        virtual void print(){
            cout << "Base2::print\n";
        }
        virtual void g(){
            cout << "Base2::g\n";
        }
};
class Derived:public Base1,public Base2{
    int iDerived;
    public:
        Derived(int i=2):iDerived(i){}
        virtual ~Derived(){
            cout << "Derived destoried\n";
        }
        virtual void print(){
            cout << "Derived::print\n";
        }
        virtual void m(){
            cout << "Derived::m\n";
        }
};
int main(){
    Derived d(100);
    typedef void (*Func)(void);
    Func f;
    //f=(Func)*((int*)*(int*)&d+1); //这是第一个虚表中的第一个函数,应该存放的派生类的析构函数,此处若调用f,则会引起程序崩溃
    //f();
    f=(Func)*((int*)*(int*)&d+2);   //这里应该是第一个虚表中第二个函数,为派生类中的print函数(派生类重写基类虚函数)
    f();
    f=(Func)*((int*)*(int*)&d+3);  //这里应该是第一个虚表中的第三个函数,应该是Base1中的f函数
    f();
    f=(Func)*((int*)*(int*)&d+4);    //这里应该是第一个虚表中的第四个函数,此处应该是派生类中的m函数
    f();
    f=(Func)*((int*)*((int*)&d+2)+1);  //这里是第二个虚表中的第一个函数,实验的结果应该是派生类的析构函数
    f();
    f=(Func)*((int*)*((int*)&d+2)+2);   //这里应该是派生类的print函数(派生类重写基类虚函数)
    f();
    f=(Func)*((int*)*((int*)&d+2)+3);   //这里应该是Base2中的g函数
    f();
    return 0;
}
       问题一: 如上面注释所说,我进入第一个虚表的第一个函数处执行,也就是执行派生类的析构函数,编译器会报错,我觉得原因是该对象已经被释放,因此下面的执行会访问到已经释放的空间,所以程序会崩溃
        但是,我进入第二个虚表的第一个函数处执行,同样也是派生类的析构函数,编译器编译通过且顺利执行,我有点儿迷惑这是为什么?
       问题二:我在有些博客上看到,在虚表的第一项存放的是对象的RTTI信息,我在Codeblocks上实验确实是这样,我想问的是,第一项存放的到底是怎么信息?是指向type_info对象的指针么?
       另外,在单继承情况下,派生类对象的虚表的第一项是派生类的运行时类型识别信息,那么在多继承情况下,这里假设是两个基类,在派生类的对象的中,有两个虚表,这两个虚表的第一项分别是两个基类的运行时类型识别信息,那么派生类的运行时类型识别信息怎么识别?
------解决思路----------------------
引用:
又来一个猜测虚表的家伙,还跟上次那个一样:
 1. 以为 成员函数指针大小与int一样大
 2. 以为 成员函数能直接转换成一般函数
 3. 以为 成员函数不会传递this指针
 4. 以为 所有编译器实现都一样


1. 成员函数大小和 int 不一样大,但虚函数表中的 slot 是和 int 一样大的。
2. 成员函数指针不能转成一般函数来调用,但是虚函数表中的函数地址是可以当成普通函数来调用的,只要把参数处理好。
3. 对于没有访问成员变量的函数来说,如果直接调用函数(即没有通过虚函数表来调用函数),有没有 this 指针是无所谓的。
4. 不能因为各个编译器实现不一样就放弃探索, 正如你发的那张表一样,每个编译器实现都不一样,还是有人去把它探索出来了,而且在这个基础上实现了高效的委托,可见这些探索还是很有意义的。
------解决思路----------------------
引用:
Quote: 引用:

又来一个猜测虚表的家伙,还跟上次那个一样:
 1. 以为 成员函数指针大小与int一样大
 2. 以为 成员函数能直接转换成一般函数
 3. 以为 成员函数不会传递this指针
 4. 以为 所有编译器实现都一样


1. 成员函数大小和 int 不一样大,但虚函数表中的 slot 是和 int 一样大的。
2. 成员函数指针不能转成一般函数来调用,但是虚函数表中的函数地址是可以当成普通函数来调用的,只要把参数处理好。
3. 对于没有访问成员变量的函数来说,如果直接调用函数(即没有通过虚函数表来调用函数),有没有 this 指针是无所谓的。
4. 不能因为各个编译器实现不一样就放弃探索, 正如你发的那张表一样,每个编译器实现都不一样,还是有人去把它探索出来了,而且在这个基础上实现了高效的委托,可见这些探索还是很有意义的。


1.虚函数表中的 slot 和 int 一样大的? 不应该是和size_t一样大么.
2. 是的,但是微软x86的__thiscall是this指针是靠寄存器传递的,纯靠c++没法完成.
3. 同上,x86上因为调用约定产生崩溃而发帖询问的人有不少.
4. 没说没有意义,自己主要就是用COM组件,基本就违背了"猜测虚表实现",只是对光用一段代码就
说虚函数怎么这么样的说:这样不好,要考虑完整