多态(C++)

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;
}

   
图 多继承的对象模型
3. 菱形继承   
菱形继承有二义性,看段代码
#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;
}

一经编译会报错



猜你喜欢

转载自blog.csdn.net/ijn842/article/details/80936236