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