类的虚表 && 多种继承模式的对象模型

在vs2013环境下。

1,类的虚表的概念

如果一个类中有虚函数,那么这个类就对应一个虚函数表,虚函数表中的元素是一组指向函数的指针每个指针指向一个虚函数的入口地址。在访问虚函数时,通过虚函数表进行函数调用。

2:,当基类中虚函数时,为什么类的大小为什么会增加4个字节?

结论 :如果类内有虚函数,则类的大小加上4个字节,是一个指针,用来指向一个函数指针数组,这个函数指针数组的每个元素就是每个虚函数的函数指针

3.基类虚表:按虚函数在类中声明的次序依次添加到虚表中

4,单继承—>派生类虚表  : 将基类的虚表拷贝一份,如果在派生类重写某个虚函数,使用派生类自己的虚函数替换派生类虚表中相同偏移量位置的虚函数。

5,验证基类虚表,并验证派生类重写虚函数后,派生类虚表的变化

  • //基类,有两个虚函数 
    #include<string>
    
    class Base
    {
    public:
    	virtual void TestFunc1()
    	{
    		cout << "TestFunc1" << endl;
    	}
    
    	 virtual void TestFunc2()
    	{
    		cout << "TestFunc2" << endl;
    	}
    
    	int _b;
    
    };
    
    //定义一个函数指针
    typedef void(*PVTF)();
    
    //通过对象的地址,找到与其对应的虚函数指针数组,并且用函数指针执行函数
    void PrintVTF(Base& b,const string& str)
    {
    	PVTF* pVTF = (PVTF*)(*(int*)&b);
    	//(int *)(&b) 将b的地址转为int型,以便将来解引用时只拿到函数指针数组的地址
    	//(*(int *)&b) 解引用int型的地址,拿到函数指针数组的地址
    	//(PVTF*)(*(int*)&b) 将函数指针数组的地址转为虚函数指针
    	//PVTF* pVTF = (PVTF*)(*(int*)&b); 将虚函数指针赋给pVTF
    	cout << str << endl;
    	while (*pVTF)
    	{
    		(*pVTF)();
    		//(*pVTF)(); 函数指针解引用,得到函数名,也就是第一个函数指针,也就执行了函数
    
    		++pVTF;
    	}
    }
    
    //派生类:公有继承基类
    class Deriver : public Base
    {
    public:
    
    void TestFunc2()   派生类对基类的虚函数进行重写
    	{
    		cout << "Dervied::TestFunc2" << endl;
    	}
    
    
    virtual void TestFunc4()  派生类独有的虚函数
    	{
    		cout << "Dervied::TestFunc3" << endl;
    	}
    
    	int _d;
    };
    
    int main()
    {
    	cout << sizeof(Base)<<endl;
    	cout << sizeof(Deriver) << endl;
    
    	Base b;
    	b._b = 1;
    
    	Deriver d;
    	d._b = 1;
    	d._d = 2;
    
        
    	PrintVTF(b,"Base VTF");
    	PrintVTF(d,"Derived VTF");
    	return 0;
    }

    执行结果:

虚函数的调用过程

1,从对象的前4个字节取虚表的地址

2,传递this指针

3,从虚表中找对应的虚函数

4,执行当前的虚函数

多继承时派生类的独有虚函数在哪个虚表中?

被继承的两个类中都有虚函数时,派生类有两个虚表,将派生类中新增的虚函数放到第一张虚表的最后。

带有虚函数的虚拟继承

1,派生类中没有新增的虚函数

class A
{
public:
	virtual void TestFunc1()
	{
		cout << "A::TestFunc1" << endl;
	}
	virtual void TestFunc2()
	{
		cout << "B::TestFunc2" << endl;
	}

	int _a;

};

class B:virtual public A
{
public:
	

	int _b;
};

int main()
{
	B b;
	b._a = 1;
	b._b = 2;
	cout << sizeof(b) << endl;
	return 0;

}

结果发现,多了偏移量指针,多了虚表指针,基类在下,派生类在上。

2,派生类中有新增的虚函数

class A
{
public:
	virtual void TestFunc1()
	{
		cout << "A::TestFunc1" << endl;
	}
	virtual void TestFunc2()
	{
		cout << "A::TestFunc2" << endl;
	}

	int _a;

};

class B:virtual public A
{
public:
	virtual void TestFunc3()   派生类新增了一个虚函数
	{
		cout << "B::TestFunc3" << endl;
	}

	int _b;
};

int main()
{
	B b;
	b._a = 1;
	b._b = 2;
	cout << sizeof(b) << endl;
	return 0;

}


运行结果: 20  比没有新增虚函数时多了4个字节

为啥会多了4个字节?这四个字节用来干什么了?

多出来的四个字节用来存放新增虚函数指针,普通继承是将新增的虚函数 指针放到基类的虚函数表最下边。这是区别。

带虚函数的虚拟菱形继承

1,没有显式给出构造或者析构函数

class A
{
public:
	virtual void TestFunc1()
	{
		cout << "A::TestFunc1" << endl;
	}
	virtual void TestFunc2()
	{
		cout << "A::TestFunc2" << endl;
	}

	int _a;

};

class B1:virtual public A
{
public:
	virtual void TestFunc1()
	{
		cout << "B1::TestFunc1" << endl;
	}

	virtual void TestFunc3()
	{
		cout << "B1::TestFunc3" << endl;
	}


	int _b1;
};

class B2 :virtual public A
{
public:
	virtual void TestFunc2()
	{
		cout << "B2::TestFunc2" << endl;
	}

	virtual void TestFunc5()
	{
		cout << "B2::TestFunc5" << endl;
	}

	int _b2;
};

class D :public B1, public B2
{
public:
	virtual void TestFunc1()
	{
		cout << "D::TestFunc1" << endl;
	}
	virtual void TestFunc6()
	{
		cout << "D::TestFunc6" << endl;
	}
	int _d;
};


int main()
{
	D d;
	d._b1 = 1;
	d._b2 = 2;
	d._d = 3;
	d._a = 4;
	cout << sizeof(d) << endl;
	return 0;

}

这个模型几乎将继承和多态的所有知识都用到了,单继承,多继承,虚拟继承。虚拟继承的基类在派生类中只能出现一次。

多态的缺陷

1,程序的运行效率降低了。普通函数函数的调用指令在编译时就确定了, 多态调用虚函数时需要通过虚表来调用函数,时间长,速度慢。

猜你喜欢

转载自blog.csdn.net/wm12345645/article/details/81326511