【C/C++】【对象模型】虚函数

虚函数表指针位置分析

类:有虚函数,会产生一个虚函数表;

类对象:有一个指针,指针vptr会指向虚函数表的开始地址;

虚函数指针的位置

虚函数表位于整个对象模型的顶端;

// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
class A
{
public:
    virtual void func() {};

public:
    int i;
};

int main()
{
    A a;
    cout << sizeof(a) << endl;

    char* p = reinterpret_cast<char*>(&a);
    char* q = reinterpret_cast<char*>(&a.i);

    if (p == q) //如果二者位置相同,说明i在对象的上面;虚函数表指针vptr在下面;
        cout << "yes" << endl;
    else
        cout << "no" << endl; 
    return 0;
}

继承关系作用下虚函数的手工调用

// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
class Base
{
public:
    virtual void func()
    {
        cout << "Base::void func()" << endl;
    }

    virtual void gunc()
    {
        cout << "Base::void gunc()" << endl;
    }


    virtual void hunc()
    {
        cout << "Base::void hunc()" << endl;
    }
};


class Derive :public Base
{
public:
    virtual void gunc()
    {
        cout << "Derive::void hunc()" << endl;
    }
};

int main()
{
    //直接用类名和用对象一样,都是这个类的对象的大小
    cout << sizeof(Base) << endl; //父类 4字节
    cout << sizeof(Derive) << endl; //子类 4字节
    
    
    Derive* d = new Derive(); //派生类指针
    long* pvptr = (long*)d; //指向对象的指针d转成了long*类型
    long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针



    for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
    {
        printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
    }
    
    typedef void(*Func)(void); //定义一个函数指针类型
    Func f = (Func)vptr[0]; //f是函数指针变量,vptr[0]是指向第一个虚函数;
    Func g = (Func)vptr[1]; //f是函数指针变量,vptr[1]是指向第二个虚函数;
    Func h = (Func)vptr[2]; //f是函数指针变量,vptr[2]是指向第三个虚函数;
    
    f();
    g();
    h();


    cout << "---------------------------------------------------------------------" << endl;


    Base* b = new Base(); //基类指针
    long* pvptr_base = (long*)b; //指向对象的指针b转成了long*类型
    long* vptr_base = (long*)(*pvptr_base);

    for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
    {
        printf("Base[%d] = 0x:%p\n", i, vptr_base[i]);
    }
    Func f_base = (Func)vptr_base[0]; //f_base是函数指针变量,vptr_base[0]是指向第一个虚函数;
    Func g_base = (Func)vptr_base[1]; //g_base是函数指针变量,vptr_base[1]是指向第二个虚函数;
    Func h_base = (Func)vptr_base[2]; //h_base是函数指针变量,vptr_base[2]是指向第三个虚函数;
    f_base();
    g_base();
    h_base();

    return 0;
}

虚函数表分析

  • 一个类只有包含虚函数才存在虚函数表,同属于一个类的对象共享虚函数表,但是有各自的vptr(虚函数指针),所指向的虚函数的入口地址都是相同的;

    // objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include <iostream>
    using namespace std;
    class Base
    {
    public:
        virtual void func()
        {
            cout << "Base::void func()" << endl;
        }
    
        virtual void gunc()
        {
            cout << "Base::void gunc()" << endl;
        }
    
    
        virtual void hunc()
        {
            cout << "Base::void hunc()" << endl;
        }
    };
    
    
    class Derive :public Base
    {
    public:
        virtual void gunc()
        {
            cout << "Derive::void hunc()" << endl;
        }
    };
    
    int main()
    {
        //直接用类名和用对象一样,都是这个类的对象的大小
        cout << sizeof(Base) << endl; //父类 4字节
        cout << sizeof(Derive) << endl; //子类 4字节
          
        Derive* d = new Derive(); //派生类指针
        long* pvptr = (long*)d; //指向对象的指针d转成了long*类型
        long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
    
        for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
        {
            printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
        }
        
        Derive* d_2 = new Derive(); //派生类指针
        long* pvptr_2 = (long*)d_2; //指向对象的指针d转成了long*类型
        long* vptr_2 = (long*)(*pvptr_2); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
    
        for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
        {
            printf("vptr_2[%d] = 0x:%p\n", i, vptr_2[i]);
        }
    
        return 0;
    }
    
    
  • 父类中有虚函数,子类中有虚函数;即父类中有虚函数表,子类中肯定也有虚函数表;

  • 无论是父类还是子类都会只有一个虚函数表,不能认为子类中有一个虚函数表 + 父类中一个虚函数表,得到子类中有两个虚函数表;

    子类中是否可能有多个虚函数表?

  • 如果子类中完全没有新的虚函数,则我们可以认为子类的虚函数表和父类的虚函数表内容相同;但仅仅是内容相同,这两个表在内存中处于不同位置;

  • 虚函数表中每一项,保存一个虚函数的入口地址,如果子类的虚函数表某项和父类虚函数表某项相同(这表示子类没有覆盖父类的虚函数);

  • 超出虚函数表部分内容不可知;无实际意义;

// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//基类不同对象虚函数指针
//基类和父类对象 虚函数指针

#include <iostream>
using namespace std;
class Base
{
public:
    virtual void func()
    {
        cout << "Base::void func()" << endl;
    }

    virtual void gunc()
    {
        cout << "Base::void gunc()" << endl;
    }


    virtual void hunc()
    {
        cout << "Base::void hunc()" << endl;
    }
};


class Derive :public Base
{
public:
    virtual void gunc()
    {
        cout << "Derive::void hunc()" << endl;
    }
};

int main()
{
    //直接用类名和用对象一样,都是这个类的对象的大小
    cout << sizeof(Base) << endl; //父类 4字节
    cout << sizeof(Derive) << endl; //子类 4字节
    
    
    Derive derive; //__vfptr = 0x00f89b90 {objModel.exe!void(* Derive::`vftable'[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
    long* p_vptr_derive = (long*)(&derive); //指向对象的指针d转成了long*类型
    long* vptr_derive = (long*)(*p_vptr_derive); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针

    typedef void(*Func)(void); //定义一个函数指针类型
    Func f = (Func)vptr_derive[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
    Func g = (Func)vptr_derive[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
    Func h = (Func)vptr_derive[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;



    Derive derive_2 = derive; //调用拷贝构造函数  __vfptr = 0x00f89b90 {objModel.exe!void(* Derive::`vftable'[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
    long* p_vptr_derive_2 = (long*)(&derive_2); //指向对象的指针d转成了long*类型
    long* vptr_derive_2 = (long*)(*p_vptr_derive_2); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针


    Func f_2 = (Func)vptr_derive_2[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
    Func g_2 = (Func)vptr_derive_2[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
    Func h_2 = (Func)vptr_derive_2[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;



    //直接用子类对象给父类对象值,子类中属于分类的那部分内容会被编译器自动区分出来,并拷贝给了父类对象
    //所以Base base = derive; 实际上做了两件事
    //1. 生成一个base对象
    //2. 用derive来初始化base对象的值,编译器做了选择,derive的虚函数表指针值并没有覆盖base对象的虚函数表指针值;
    Base base = derive; //直接用子类对象给父类对象值 __vfptr = 0x00f89b34 {objModel.exe!void(* Base::`vftable'[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
    long* p_vptr_base = (long*)(&base); //指向对象的指针d转成了long*类型
    long* vptr_base = (long*)(*p_vptr_base); //(*

    Func f_base = (Func)vptr_base[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
    Func g_base = (Func)vptr_base[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
    Func h_base = (Func)vptr_base[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;


    //面向对象oo和基于对象ob
    // 1. C++通过类的指针和引用来支持多态,就是面向对象OO;
    // 2. OB,也叫ADT抽象数据模型,不支持多态,执行速度更快,因为函数调用的解析不需要运行时决定(没有多态)
    //    因为函数调用的解析不需要运行时决定(没有多态),而是在编译器期间就解析完成,内存空间紧凑程度上更紧凑,因为没有虚函数指针和虚函数表这些概念;
    //    但是OB的设计灵活性就差;

    //C++既支持OO,有支持OB;

    return 0;
}

多重继承虚函数表分析

  • 一个对象,如果它的类有多个基类则有多个虚函数表指针(注意是两个虚函数表指针,而不是两个虚函数表);

  • 在多继承中,对应各个基类的vptr按照继承顺序依次放置在类的内存空间中,且子类与第一个基类公用一个vptr(第二个基类有自己的vptr);

  • 子类对象有两个虚函数表指针,vptr1和vptr2;

  • 类Derived有两个虚函数表,因为它继承自两个基类;

  • 子类和第一个基类公用一个vptr(应为vptr指向一个虚函数表,所以也可以说子类和第一个基类公用一个虚函数表vtb1)

    因为我们注意到类Dreived的虚函数表1里面的5个函数u,g正好是基类1里面的函数;

  • 子类中的虚函数覆盖了父类中的同名虚函数;


#include <iostream>
using namespace std;



class Base_1
{
public:
	virtual void func()
	{
		cout << "Base_1::void func()" << endl;
	}

	virtual void gunc()
	{
		cout << "Base_1::void gunc()" << endl;
	}
};



class Base_2
{
public:
	virtual void hunc()
	{
		cout << "Base_2::void func()" << endl;
	}

	virtual void iunc()
	{
		cout << "Base_2::void gunc()" << endl;
	}
};




class Derived :public Base_1, public Base_2
{
public:
	virtual void func()
	{
		cout << "Derived::void func()" << endl;
	}
	virtual void iunc()
	{
		cout << "Derived::void iunc()" << endl;
	}



	//自己的虚函数
	virtual void munc()
	{
		cout << "Derived::void munc()" << endl;
	}
	virtual void nunc()
	{
		cout << "Derived::void nunc()" << endl;
	}
	virtual void junc()
	{
		cout << "Derived::void junc()" << endl;
	}
};

int main()
{
	//多重继承虚函数表分析
	cout << sizeof(Base_1) << endl;
	cout << sizeof(Base_2) << endl;
	cout << sizeof(Derived) << endl;

	Derived derive;
	Base_1& b_1 = derive;
	Base_2& b_2 = derive;
	Derived& d = derive;


	typedef void(*Func)(void);

	long* p_derived_1 = (long*)(&derive);
	long* vptr_1 = (long*)(*p_derived_1); //取第一个虚函数表指针



	long* p_derived_2 = p_derived_1 + 1; 
	long* vptr_2 = (long*)(*p_derived_2); //取第二个虚函数表指针



	Func f_1_1 = (Func)vptr_1[0]; //f_1_1 = 0x0039146f {objModel.exe!Derived::func(void)}
	Func f_1_2 = (Func)vptr_1[1]; //f_1_2 = 0x00141460 {objModel.exe!Base_1::gunc(void)}
	Func f_1_3 = (Func)vptr_1[2]; //f_1_3 = 0x00141483 {objModel.exe!Derived::munc(void)}
	Func f_1_4 = (Func)vptr_1[3]; //f_1_4 = 0x00141492 {objModel.exe!Derived::nunc(void)}
	Func f_1_5 = (Func)vptr_1[4]; //f_1_5 = 0x0014146a {objModel.exe!Derived::junc(void)}

	Func f_2_1 = (Func)vptr_2[0]; //f_2_1 = 0x00141479 {objModel.exe!Base_2::hunc(void)}
	Func f_2_2 = (Func)vptr_2[1]; //f_2_2 = 0x0014145b {objModel.exe!Derived::iunc(void)}

	f_1_1();
	f_1_2();
	f_1_3();
	f_1_4();
	f_1_5();

	cout << "-----------------------------" << endl;

	f_2_1();
	f_2_2();


	//

	return 0;
}

vptr/vtbl创建时机

辅助工具: **cl.exe ** 编译链接工具

虚函数调用问题

猜你喜欢

转载自www.cnblogs.com/Trevo/p/13367558.html