C++对象模型(剖析虚函数表)

        多态是C++中一种代码复用的手段,什么叫多态?简单点说,就是多种形态。那么C++中是怎样实现多态的呢?

        在C++中,多态分为两种:静态多态与动态多态。静态多态是对象声明的类型是在编译时确定的,比如说函数重载,模板。动态多态是所指对象的类型是在运行时确定的。动态多态的实现是通过虚函数来完成的。我们可以通过基类的指针或引用来调用基类与子类的函数,这个过程就实现了多态。我们知道,多态是通过虚函数来实现的,那虚函数的背后又做了哪些事情呢,本篇文章主要来探讨虚函数的背后-->虚函数表。

一.普通继承(有虚函数,有成员变量,无虚函数覆盖)

在此种继承中,子类只继承于一个父类,没有对父类的虚函数进行重写,子类与父类都有各自的成员变量。

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base
{
public :
	Base()
		:_b(10)
	{}
	 virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base::func2()" << endl;
	}
private :
	int _b;
};
class Deriver:public Base
{
public:
	Deriver()
		:_d(20)
	{}
	virtual void func3()
	{
		cout << "Deriver::func3()" << endl;
	}
	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base b;
	Deriver d;
	PrintVTable(*(int *)&b);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d);
	getchar();
	return 0;
}

我们可以通过监视窗口查看,大致的了解此种继承的对象模型。


         通过以上分析,我们可以知道,多态的背后是依靠虚函数表去实现的,虚函数表里面存储的是每个虚函数的地址,顺序按照声明顺序排列。其中this指针里面保存的是虚函数指针的地址。

下面是派生类的对象模型:



从以上模型中我们可以得出以下结论:

1.在对象模型中,起始的四个字节用来存放虚函数指针,其指向虚函数表。

2.成员变量按照其继承与生命的顺序依次存放。

3.父类的虚函数放在子类的虚函数前面。

二.单继承(有成员变量,有虚函数,有虚函数覆盖)

在此种继承中,子类只有一个父类,子类的成员函数有对父类的成员函数进行了重写,有各自的成员变量。

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base
{
public :
	Base()
		:_b(10)
	{}
	 virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base::func2()" << endl;
	}
private :
	int _b;
};
class Deriver:public Base
{
public:
	Deriver()
		:_d(20)
	{}
	virtual void func1()
	{
		cout << "Deriver::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Deriver::func3()" << endl;
	}
	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base b;
	Deriver d;
	PrintVTable(*(int *)&b);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d);
	getchar();
	return 0;
}

我们可以通过调试窗口来观察子对象的大概模型。

下面是派生类的对象模型:

                                                                              

从以上模型中,我们可以得出以下结论:

当子类中有和父类同名的虚函数时,会形成覆盖,父类同名虚函数的地址会被子类同名函数的地址替换掉。此时用下面这种程序对派生类进行访问时,

Base *p=new Deriver;
p->func1();
调用的是派生类Deriver::func1(),即实现了多态。

三.多重继承(有成员变量,有虚函数,无虚函数覆盖)

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base1
{
public :
	Base1()
		:_b1(10)
	{}
	 virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	
private :
	int _b1;
};
class Base2
{
public:
	Base2()
		:_b2(20)
	{}
	virtual void func2()
	{
		cout << "Base2::func2()" << endl;
	}
private:
	int _b2;
};
class Deriver:public Base1,public Base2
{
public:
	Deriver()
		:_d(30)
	{}
	virtual void func3()
	{
		cout << "Deriver::func3()" << endl;
	}
	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base1 b1;
	Base2 b2;
	Deriver d;
	PrintVTable(*(int *)&b1);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&b2);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d);
	getchar();
	return 0;
}


子类对象模型如下:

                                               

总结:

1.每个父类都有自己的虚表

2.子类的成员函数被放在第一个父类的虚函数表中

3.内存布局中,父类按照声明的顺序排列(可以将Base1与Base2的顺序调换做一下测试)


四.多重继承(有成员变量,有虚函数,有虚函数覆盖)

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base1
{
public :
	Base1()
		:_b1(10)
	{}
	 virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
private :
	int _b1;
};
class Base2
{
public:
	Base2()
		:_b2(20)
	{}
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base2::func2()" << endl;
	}
private:
	int _b2;
};
class Deriver:public Base1,public Base2
{
public:
	Deriver()
		:_d(30)
	{}
	virtual void func1()
	{
		cout << "Deriver::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Deriver::func3()" << endl;
	}
	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base1 b1;
	Base2 b2;
	Deriver d;
	PrintVTable(*(int *)&b1);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&b2);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d);
	getchar();
	return 0;
}




从以上分析中,我们可以看出子类自己的成员函数是放在Base1的虚函数表里的,子类中重写的Deriver::func1()将Base1与Base2中的func1()都覆盖了。

子对象模型如下:

                 

总结:

1.每个父类都有自己的虚表

2.子类的成员函数被放在第一个父类的虚函数表中

3.内存布局中,父类按照声明的顺序排列(可以将Base1与Base2的顺序调换做一下测试)

4.每个父类的func1()都被覆盖成了子类的func1(),保证了不同父类的指针指向 同一个子类实例,实现了多态。 

五.重复继承

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base
{
public :
	Base()
		:_b(10)
	{}
	 virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	
private :
	int _b;
};
class Base1:public Base 
{
public:
	Base1()
		:_b1(20)
	{}
	virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
private:
	int _b1;
};
class Base2:public Base
{
public:
	Base2()
		:_b2(30)
	{}
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Base2::func3()" << endl;
	}
private:
	int _b2;
};
class Deriver:public Base1,public Base2
{
public:
	Deriver()
		:_d(40)
	{}
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}

	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base b;
	Base1 b1;
	Base2 b2;
	Deriver d;
	PrintVTable(*(int *)&b);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&b1);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&b2);
	cout << "----------------" << endl;
 	PrintVTable(*(int *)&d);

	getchar();
	return 0;


内存分布如下:


    子对象模型如下:

        

                                                   


从以上模型我们可以看出,最顶端父类的数据成员被子类Deriver继承了两次,其中一次由Base1继承而来,另一次由Base2继承而来,而子类Deriver又继承于Base1与Base2 ,所以在子对象模型中,进行如下访问时,就会产生二义性。

Deriver d;
d._b=10;//产生二义性
d.Base1::_b=10;
d.Base2::_b=10;//正确
所以在子对象模型中,_d存储了两份,造成了数据冗余,这种继承我们成为重复继承。

六.重复虚拟继承(钻石继承)

重复虚拟继承是用来解决重复继承当中所造成的数据冗余问题,当在Base1和Base2的继承方式前加上virtual就是虚拟继承。

#include<iostream>
using namespace std;
typedef void(*FUNC)();
void PrintVTable(int p)
{
	int *ptr = (int*)p;
	FUNC fun = NULL;
	int i = 0;
	while (ptr[i])
	{
		fun = (FUNC)ptr[i];
		fun();
		i++;
	}
}
class Base
{
public :
	Base()
		:_b(10)
	{}
	 virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	
private :
	int _b;
};
class Base1:virtual public Base 
{
public:
	Base1()
		:_b1(20)
	{}
	virtual void func1()
	{
		cout << "Base1::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base1::func2()" << endl;
	}
private:
	int _b1;
};
class Base2:virtual public Base
{
public:
	Base2()
		:_b2(30)
	{}
	virtual void func1()
	{
		cout << "Base2::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Base2::func3()" << endl;
	}
private:
	int _b2;
};
class Deriver:public Base1,public Base2
{
public:
	Deriver()
		:_d(40)
	{}
	virtual void func1()
	{
		cout << "Derive::func1()" << endl;
	}

	virtual void func4()
	{
		cout << "Deriver::func4()" << endl;
	}
private:
	int _d;
};
int main()
{
	Base b;
	Base1 b1;
	Base2 b2;
	Deriver d;
	PrintVTable(*(int *)&d);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d+3);
	cout << "----------------" << endl;
	PrintVTable(*(int *)&d+8);

	getchar();
	return 0;
}
内存布局分析如下:



                                              

                                                         

子对象模型为:

                                                                                                     


         从上面我们可以看出重复虚拟继承的时候,最顶端的父类Base是在最下面存放的,并且Base1与Base2的虚函数表中并没有Base类的成员,其依靠一个地址来寻找Base的位置。

         需要注意的是不同环境下,内存模型会有所不同,需要自己去测试,此处编译环境为vs2013。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值