多态

来源:互联网 发布:淘宝网高跟靴 编辑:程序博客网 时间:2024/06/07 04:38

   1、多态性 
  指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。 
  a、编译时多态性:通过重载函数实现 
  b、运行时多态性:通过虚函数实现。



C++用虚函数实现多态

父类里定义了虚函数

子类重写虚函数


THEN:

Animal A= new dog()

Animal A = new cat()

实现多态





重写和重载



C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

1.虚函数(impure virtual)

  C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现

  子类可以重写父类的虚函数实现子类的特殊化。

  如下就是一个父类中的虚函数:

复制代码
class A{public:    virtual void out2(string s)    {        cout<<"A(out2):"<<s<<endl;    }};
复制代码

2.纯虚函数(pure virtual)

  C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。

  C++中的纯虚函数更像是“只提供申明,没有实现”,是对子类的约束,是“接口继承”。

  C++中的纯虚函数也是一种“运行时多态”。

  如下面的类包含纯虚函数,就是“抽象类”:

复制代码
class A{public:    virtual void out1(string s)=0;    virtual void out2(string s)    {        cout<<"A(out2):"<<s<<endl;    }};
复制代码

3.普通函数(no-virtual)

  普通函数是静态编译的,没有运行时多态,只会根据指针或引用的“字面值”类对象,调用自己的普通函数

  普通函数是父类为子类提供的“强制实现”。

  因此,在继承关系中,子类不应该重写父类的普通函数,因为函数的调用至于类对象的字面值有关。

4.程序综合实例

复制代码
#include <iostream>using namespace std;class A{public:    virtual void out1()=0;  ///由子类实现    virtual ~A(){};    virtual void out2() ///默认实现    {        cout<<"A(out2)"<<endl;    }    void out3() ///强制实现    {        cout<<"A(out3)"<<endl;    }};class B:public A{public:    virtual ~B(){};    void out1()    {        cout<<"B(out1)"<<endl;    }    void out2()    {        cout<<"B(out2)"<<endl;    }    void out3()    {        cout<<"B(out3)"<<endl;    }};int main(){    A *ab=new B;    ab->out1();    ab->out2();    ab->out3();    cout<<"************************"<<endl;    B *bb=new B;    bb->out1();    bb->out2();    bb->out3();    delete ab;    delete bb;    return 0;}
复制代码

执行结果:

 

动态多态:

使用时在父类中写一个虚函数,在子类中分别重写,用这个父类指针调用这个虚函数,它实际上会调用各自子类重写的虚函数。

 动态多态:    是指在程序运行时才能确定函数和实现的链接,此时才能确定调用哪个函数,父类指针或者引用能够指向子类对象,调用子类的函数,所以在编译时是无法确定调用哪个函数

vector<Animal*>anims;

              Animal * anim1 =new Dog;

              Animal * anim2 =new Cat;

               //处理异质类集合

              anims.push_back(anim1);

              anims.push_back(anim2); 

           缺点:运行期间进行虚函数绑定,提高了程序运行开销;庞大的类继承层次,对接口的修改易影响类继承层次;由于虚函数在运行期才绑定,所以编译器无法对虚函数进行优化

















C++对象模型可以概括为以下2部分:

1.       语言中直接支持面向对象程序设计的部分

2.       对于各种支持的底层实现机制

语言中直接支持面向对象程序设计的部分,如构造函数析构函数虚函数继承(单继承、多继承、虚继承)、多态等等,




静态绑定与动态绑定

程序调用函数时,将使用那个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解析为执行特定的函数代码块被称为函数名绑定(binding,又称联编)。C语言中,这非常简单,因为每个函数名都对应一个不同的额函数。在C++中,由于函数重载的缘故,这项任务更复杂。编译器必须查看函数参数以及函数名才能确定使用哪个函数。然而编译器可以再编译过程中完成这种绑定,这称为静态绑定(static binding),又称为早期绑定(early binding

然而虚函数是这项工作变得更加困难。使用哪一个函数不是能在编译阶段时确定的,因为编译器不知道用户将选择哪种类型所以,编译器必须能够在程序运行时选择正确的虚函数的代码,这被称为动态绑定(dynamic binding),又称为晚期绑定(late binding





从单继承可以知道,派生类中只是扩充了基类的虚函数表。如果是多继承的话,又是如何扩充的?

1)       每个基类都有自己的虚表。

2)       子类的成员函数被放到了第一个基类的表中。

3)       内存布局中,其父类布局依次按声明顺序排列。

4)       每个基类的虚表中的print()函数都被overwrite成了子类的print ()。这样做就是为了解决不同的基类类型的指针指向同一个子类实例,而能够调用到实际的函数







多态(Polymorphisn)在C++中是通过虚函数实现的。通过前面的模型【参见有重写的单继承】知道,如果类中有虚函数,编译器就会自动生成一个虚函数表,对象中包含一个指向虚函数表的指针。能够实现多态的关键在于虚函数是允许被派生类重写的,在虚函数表中,派生类函数对覆盖(override)基类函数。除此之外,还必须通过指针或引用调用方法才行,将派生类对象赋给基类对象。




==============================================================================================================================

===================================================================================================================================

==================================================================================================================================




一、重写(override)

override是重写(覆盖)了一个方法,以实现不同的功能。一般是用于子类在继承父类时,重写(重新实现)父类中的方法。

重写(覆盖)的规则:

   1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载.

   2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。

   3、重写的方法的返回值必须和被重写的方法的返回一致;

   4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;

   5、被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没s有对其进行重写。

   6、静态方法不能被重写为非静态的方法(会编译出错)。

View Code

 二、overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。

重载的规则

   1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样);

   2、不能通过访问权限、返回类型、抛出的异常进行重载;

   3、方法的异常类型和数目不会对重载造成影响;

多态的概念比较复杂,有多种意义的多态,一个有趣但不严谨的说法是:继承是子类使用父类的方法,而多态则是父类使用子类的方法。

一般,我们使用多态是为了避免在父类里大量重载引起代码臃肿且难于维护。

举个例子:

View Code

注意Triangle类的方法是重写,而Rectangle类的方法是重载。对两者比较,可以发现多态对重载的优点:

如果用重载,则在父类里要对应每一个子类都重载一个取得边数的方法;

如果用多态,则父类只提供取得边数的接口,至于取得哪个形状的边数,怎样取得,在子类里各自实现(重写)


原创粉丝点击