比如举个例子:见人说人话,见鬼说鬼话
2>动态多态
我们先来看一段代码:
#define _CRT_SECURE_NO_WARNING 1
#include
#include
#include
#include
#include
using namespace std;
class FittingRoom
{
public:
void GoToManFittingRoom()
{
cout<<"Man--->Please go to left"<Please go to right"<GotoFitttingRoom(ft);
delete p;
Sleep(1000);
}
}
int main()
{
TestFittingRoom();
return 0;
}
在该段代码里,我们实现了一个类FittingRoom(试衣间),试衣间分为男女试衣间,如果是男则去左边的试衣间,如果是女则去右边的试衣间。
上述便是动态多态实现的一种实现场景
(1)动态多态的实现条件: --基类中必须包含虚函数,并且派生类一定要对基类中的虚函数进行重写
--通过基类对象的指针或引用调用虚函数
所谓重写是:a.基类函数必须为虚函数
b.派生类函数必须与基类中的虚函数保持一致(包括返回类型、函数名称、参数列表)
但是有例外:即协变:第一种是:基类虚函数必须返回基类对象的指针/引用,派生类虚函数返回派生类对象的指针/引用
第二种是:虚拟的析构函数(基类和派生类的析构函数名字不相同)
第三种是:派生类的虚函数可以与基类中的虚函数访问限定符不一样
下面我们来一段代码实现一下重写以及特殊的重写方式·
#include
using namespace std;
class Base
{
public:
virtual void TestFunc1(int a = 1)
{
cout<<"Base::TestFunc1()"<TestFunc1();
pb->TestFunc2(10);
pb->TestFunc3(10);
pb->TestFunc4(1);
pb = new Derived;
delete pb;
return 0;
}
调用TestFunc1时,往进一执行,发现调用的是Base基类里的
调用TestFunc2时,一执行发现调用的是也是Base基类里面的
调用TestFunc3时,一执行发现调用的是Derived派生类里面的
调用TestFunc4时,一执行发现调用的是也是Base基类里面的
协变:>>>>我们new了一个派生类对象,现在我们看看调用的虚拟的析构函数调用的哪个
我们发现调用的是派生类的析构函数
1.什么是函数重载,同名隐藏、重写
2.哪些函数不能定义为虚函数(详解看。。。。)
类的构造函数不能作为虚函数
类的拷贝构造函数也不能作为虚函数
静态成员函数也不能作为虚函数
友元函数也不能作为虚函数
赋值重载函数可以作为虚函数,但是不建议
析构函数可以用作虚函数,并且建议在带有虚函数的类中,最好将基类中的析构函数作为虚函数
在上述重写中我们发现有的调用的是基类中,有的调用的是派生类中的,那么为什么呢?下面我们就来探究一下
#include
using namespace std;
class Base
{
public:
virtual void TestFunc1()
{
cout<<"Base::TestFunc1()"<
会发现在b = 1 的前面多了一个4字节的地址,然后对该地址里面的内容进行查看,发现是这样的:
,里面存放的是两个地址,那么这两个地址是什么呢,我们打印一下运行结果是这样的:,也就是说这两个地址是虚函数的地址,所以多出来的那四个字节是虚表指针,指向该类中的虚函数地址
图 Base类的对象模型
在Base的基础上我们增加一个派生类,公有继承自Base类
#include
using namespace std;
class Base
{
public:
virtual void TestFunc1()
{
cout<<"Base::TestFunc1()"<B: ");
Derived& rd = d;
PrintVFT(rd,"D VFT--->D: ");
return 0;
}
图 派生类的对象模型
总结一下:如果是普通函数,直接调用
如果是虚函数,调用时:(1)从对象的前4个字节中取虚函数的地址
(2)传递this指针
(3)从虚表中获取虚函数的地址(在虚表地址+虚函数在虚表中的偏移量)
(4)调用虚函数
如果是派生类继承自父类,全部继承父类的所有内容,自己的和父类的普通函数直接调用就行,但是如果是虚函数: (1)父类有自己没有,虚表里存放的是父类的虚函数地址
(2)父类没有自己有,虚表里存放的是自己的虚函数地址
(3)父类有自己也有,对其构成了重写,此时存放的是派生类的
单继承对象模型上面已经论述过,不再多说
代码:
#include
using namespace std;
class B1
{
public:
virtual void TestFunc1()
{
cout<<"B1::TestFunc1()"<B1: ");
B2& rb2 = d;
PrintVFT(rb2,"D VFT--->B2: ");
D& rd = d;
PrintVFT(rd,"D VFT--->D: ");
return 0;
}
#include
using namespace std;
class B
{
public:
virtual void TestFunc1()
{
cout<<"B::TestFunc1()"<B: ");
C1& c1 = d;
PrintVFT(c1,"D VFT-->C1: ");
C2& c2 = d;
PrintVFT(c2,"D VFT--->C2: ");
D& rd = d;
PrintVFT(rd,"D VFT--->D: ");
return 0;
}
此时编译器会报错:
#include
using namespace std;
class B //1,2
{
public:
virtual void TestFunc1()
{
cout<<"B::TestFunc1()"<B: ");
C1& c1 = d;
PrintVTF(c1,"D VTF--->C1: ");
C2& c2 = d;
PrintVTF(c2,"D VFT--->C2: ");
D& rd = d;
PrintVTF(rd,"D VFT--->D: ");
return 0;
}
1>概念
在成员函数(必须为虚函数)的形参列表后面写上=0,则成员函数为虚函数,包含虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象,纯虚函数在派生类中重新定义以后,派生类才能实例化对象
2>例子
#include
using namespace std;
class Person //包含纯虚函数,不能实例化出对象
{
public:
virtual void GoToWashRom() = 0;
private:
string _strname;
};
class Man
{
public:
virtual void GoToWashRoom() //Person类中的纯虚函数在派生类中进行了重新定义,所以派生类实例化出对象
{
cout<<"Man--->Please left"<