c++的多态



一、对象的类型

   对象的类型:(1)静态类型:对象声明时的类型,是在编译时确定的;

                         (2)动态类型:目前所指的对象的类型,是在运行时确定的。

二、多态

      (1)静态多态:函数重载和泛型编程

      (2)动态多态:虚函数

三、多态类型

       1、静态多态:编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

int Add(int left, int right)
{
	return left + right;
}
float Add(float left, float right)
{
	return left + right;
}
int main()
{
	cout << Add(10, 20) << endl;
	cout << Add(13.12, 24.45) << endl;
	return 0;
}

class Animal
{
public:
	void shout()
	{
		cout << "发出叫声" << endl;
	}
};
class Dog :public Animal
{
public:
	void shout()
	{
		cout << "汪汪" << endl;
	}
};
class Cat :public Animal
{
public:
	void shout()
	{
		cout << "喵喵" << endl;
	}
};
class Bird :public Animal
{
public:
	void shout()
	{
		cout << "叽喳" << endl;
	}
};
template <typename T>
void animalShout(T & t)
{
	t.shout();
}
int main()
{
	Animal anim;
	Dog dog;
	Cat cat;
	Bird bird;
	animalShout(anim);
	animalShout(dog);
	animalShout(cat);
	animalShout(bird);
	return 0;
}
在编译之前,函数模板中t.shout() 调用的是哪一个接口并不确定。在编译期间,编译器推断出模板参数,因此确定调用的shuot是哪个具体的类型的接口。不同的推断结果调用不同的函数,这就是编译器多态。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用

        2、 动态多态:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法
       使用virtual关键字修饰函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。

#include<iostream>
#include<windows.h>
using namespace std;
class CWashRoom
{
public:
	void GoToManWashRoom()
	{
		cout << "Man--->Please Left" << endl;
	}

	void GoToWomanWashRoom()
	{
		cout << "Woman--->Please Right" << endl;
	}
};
class CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom) = 0;
};

class CMan :public CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom)
	{
		_washRoom.GoToManWashRoom();
	}
};
class CWoman :public CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom)
	{
		_washRoom.GoToWomanWashRoom();
	}
};
void FunTest()
{
	CWashRoom washRoom;
	for (int iIdx = 1; iIdx <= 10; ++iIdx)
	{
		CPerson* pPerson;
		int iPerson = rand() % iIdx;
		if (iPerson & 0x01)
		{
			pPerson = new CMan;
		}
		else
		{
			pPerson = new CWoman;
		}

		pPerson->GoToWashRoom(washRoom);
		delete pPerson;
		pPerson = NULL;
		Sleep(1000);
	}
}
int main()
{
	FunTest();
	getchar();
	return 0;
}

动态绑定条件:1、必须是虚函数

            2、通过基类类型的引用或者指针调用

class Animal
{
public:
	virtual void shout() = 0;
};
class Dog :public Animal
{
public:
	virtual void shout()
	{
		cout << "汪汪" << endl;
	}
};
class Cat :public Animal
{
public:
	virtual void shout()
	{
		cout << "喵喵" << endl;
	}
};
class Bird :public Animal
{
public:
	virtual void shout()
	{
		cout << "叽喳" << endl;
	}
};
int main()
{
	Animal * anim1 = new Dog;
	Animal * anim2 = new Cat;
	Animal * anim3 = new Bird;
	anim1->shout();
	anim2->shout();
	anim3->shout();
	return 0;
}

运行时多态的实现依赖于虚函数机制。当某个类声明了虚函数时,编译器将为该类对象安插一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数地址。运行期间通过虚函数表指针与虚函数去确定该类虚函数的真正实现

四、运行期多态和编译期多态的优缺点

1、运行期多态

优点:(1)是设计中的重要特性,对客观世界直觉认知

     (2)能够处理同一个继承体系下的异质类集合

缺点:(1)运行期间进行虚函数绑定,提高了程序的运行开销

     (2)庞大的类继承层次,对接口的修改易影响类继承层次

     (3)虚表指针增加了对象体积,类也多了一张虚函数表,造成了资源消耗

2、编译期多态

优点:(1)它带来了泛型编程的概念,使得c++拥有泛型编程和STL这样的强大武器

     (2)在编译器完成多态,提高运行期效率

     (3)具有很强的适配性与松耦合性,对于特殊类型可由模板特化、全特化处理

缺点:(1)程序可读性降低,代码调试带来困难

     (2)无法实现模板的分离编译,当工程很大时,编译时间不可忽略

     (3)无法处理异质对象集合

五:继承体系同名成员函数的关系

1、重载

(1)在同一作用域;(2)函数名相同,参数不同(参数类型不同或参数个数不同);(3)返回值可以相同,也可以不同

2、重写(覆盖)

(1)不在同一作用域(分别在基类和派生类)

(2)函数名相同,参数相同,返回值相同(协变例外)

(3)基类函数必须有virtual关键字

(4)访问修饰符可以不同

3、重定义(隐藏)

(1)在不同作用域中(分别在基类和派生类)

(2)函数名相同

(3)在基类和派生类中只要不构成重写就是重定义
六、纯虚函数

在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

class Person
{
	virtual void Display() = 0;   // 纯虚函数
protected:
	string _name;          // 姓名
};

class Student : public Person
{};

总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8、虚表是所有类对象实例共用的



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值