c++的多态

来源:互联网 发布:国内衬衫品牌 知乎 编辑:程序博客网 时间:2024/05/28 23:21


一、对象的类型

   对象的类型:(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、虚表是所有类对象实例共用的



0 0
原创粉丝点击