带有虚函数的单继承
●带有虚函数的普通单继承
#include<iostream>
using namespace std;
class Base//定义一个Base类
{
public:
virtual void BaseTest1()//Base类中定义虚函数BaseTest1()
{
cout << "Base::BaseTest1()" << endl;//函数打印它的作用域和函数名
}
virtual void BaseTest2()//Base类中定义虚函数BaseTest2()
{
cout << "Base::BaseTest2()" << endl;//函数打印它的作用域和函数名
}
int _base;//Base类内定义一个数据成员
};
class Derive : public Base//定义Derive类公有普通继承自Base类
{
public:
virtual void DeriveTest1()//Derive类中定义函数DeriveTest1()
{
cout << "Derive::DeriveTest1()" << endl;//函数打印它的作用域和函数名
}
virtual void DeriveTest2()//Derive类中定义函数DeriveTest2()
{
cout << "Derive::DervieTest2()" << endl;//函数打印它的作用域和函数名
}
int _derive;//Derive类定义一个数据成员
};
int main()
{
Derive d;//创建Derive类对象
cout << sizeof(d) << endl;//打印Derive类对象的大小
d._base = 1;//通过Dervie类对象改变继承自Base类对象的数据成员
d._derive = 2;//通过Derive类对象改变它自己的数据成员
return 0;
}
程序运行结果:
通过运行结果可以得知Dervie类对象的大小为12字节,接下来我们就来看看在这12字节内究竟存储了哪些东西?对程序进行调试,并取出Derive类对象所在的内存窗口:
我们在程序中执行了d._base=1; d._derive=2;所以0x003BF840就是继承自Base类的数据成员的地址,0x003BF844就是Derive类自己的数据成员的地址。在Derive类对象的前4个字节,存放了一个地址,查看该地址指向的位置:
因为Base类与Derive类中一共含有4个虚函数,而该地址指向的位置恰好存放了4个地址,我们可以推测这四个地址就分别是4个虚函数的函数地址,编写一个函数来验证我们的猜想:
typedef void(*PF)();//将函数指针的名字进行重命名
void FunTest(Derive& d)//定义一个函数测试函数
{
PF* PFTV = NULL;//定义一个指向函数指针的指针
PFTV = (PF*)(*(int*)&d);//将Derive类对象的前四个字节的指针强制类型转换为一个指向函数指针的指针
while (*PFTV)//设置一个循环,当函数指针保存的值为0时退出
{
(*PFTV)();//用指针来执行函数指针指向的函数
PFTV = (PF*)((int*)PFTV + 1);//执行下一个函数
}
}
将这个函数添加在主函数return 0;之前,运行程序,程序运行结果为:
程序顺利的执行了Base类和Derive类的虚函数,这也验证了我们的猜想,所以在Dervie类对象的前四个字节的指针,指向一个虚函数表:如果我们通过基类Base类创建对象,则会按照虚函数在类中声明的先后次序依次存放在虚函数表中,如果我们通过派生类Derive类生成对象,则会先将基类中的虚函数表拷贝一份,在下面按照派生类中虚函数的声明先后次序依次将派生类特有的虚函数添加在虚函数表中。
所以可以简单的画出带有虚函数的普通单继承方式下派生类的模型:
●如果我们在派生类Dervie类中对基类Base类的某个虚函数进行重写,那么在虚函数表中会怎样存放呢?
针对这个问题,我们对上述程序中的Base类中的BaseTest1()函数在派生类Derive类中进行重写来验证:
#include<iostream>
using namespace std;
class Base//定义一个Base类
{
public:
virtual void BaseTest1()//Base类中定义虚函数BaseTest1()
{
cout << "Base::BaseTest1()" << endl;//函数打印它的作用域和函数名
}
virtual void BaseTest2()//Base类中定义虚函数BaseTest2()
{
cout << "Base::BaseTest2()" << endl;//函数打印它的作用域和函数名
}
int _base;//Base类内定义一个数据成员
};
class Derive : public Base//定义Derive类公有普通继承自Base类
{
public:
virtual void BaseTest1()//对Base类的BaseTest1()函数进行重写
{
cout << "Derive::BaseTest1()" << endl;
}
virtual void DeriveTest1()//Derive类中定义函数DeriveTest1()
{
cout << "Derive::DeriveTest1()" << endl;//函数打印它的作用域和函数名
}
virtual void DeriveTest2()//Derive类中定义函数DeriveTest2()
{
cout << "Derive::DervieTest2()" << endl;//函数打印它的作用域和函数名
}
int _derive;//Derive类定义一个数据成员
};
typedef void(*PF)();//将函数指针的名字进行重命名
void FunTest(Derive& d)//定义一个函数测试函数
{
PF* PFTV = NULL;//定义一个指向函数指针的指针
PFTV = (PF*)(*(int*)&d);//将Derive类对象的前四个字节的指针强制类型转换为一个指向函数指针的指针
while (*PFTV)//设置一个循环,当函数指针保存的值为0时退出
{
(*PFTV)();//用指针来执行函数指针指向的函数
PFTV = (PF*)((int*)PFTV + 1);//执行下一个函数
}
}
int main()
{
Derive d;//创建Derive类对象
cout << sizeof(d) << endl;//打印Derive类对象的大小
d._base = 1;//通过Dervie类对象改变继承自Base类对象的数据成员
d._derive = 2;//通过Derive类对象改变它自己的数据成员
FunTest(d);
return 0;
}
程序运行结果:
我们可以看到,程序运行依然执行了4个函数,但是基类的Base::BaseTest1()函数已经被覆盖成为重写过的Derive::BaseTest1(),所以就可以得知:如果派生类重写了基类的某个虚函数,就使用派生类重写的虚函数覆盖虚函数表中相同偏移量位置的基类虚函数。
当在派生类中对基类虚函数进行重写时,派生类对象模型:
●带有虚函数的虚拟单继承
我们依然采用上面的代码,只不过将普通继承替换为虚拟继承:
#include<iostream>
using namespace std;
class Base//定义一个Base类
{
public:
virtual