第十六章 多态性

1.  相同函数名具有多态性:

  重载 覆盖  隐藏
共同点:  函数名相同 函数名相同 函数名相同
不同点: 

同类、参数类型、数量不同 

或 有无const 

不同类,同参,有基virtual 

不同类,同参,且无virtual

不同类,不同参(不论有无virtual) 

体现: 由函数调用(静态联编) 由函数调用取决于object(动态联编) 取决于pointer(不能体现多态性)

①  译时的多态(由函数名来调用时体现):重载:同类,不同参

②  运行时的多态(用指向不同类的指针来调用):

覆盖:  不同类,同参,基类有virtual(由指针指向的类型来决定,体现了多态性

隐藏:①不同类,同参,基类无virtual②不同类,不同参(不论有无virtual)(由指针来决定,不能体现多态性)

1.  为什么要使用多重继承

多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数。C++多态性是通过虚函数(virtual)来实现的。

2.  在派生类中增加函数

RTTI:  ①typeid返回指针或引用所指的实际类型

②dynamic_cast 将基类类型的指针或引用安全的转换成派生类类型的指针或引用

dynamic_cast<son*>pf->beautiful();

注意:①硬性转换尽量少用②必须使用虚函数

3.  使用多重继承

使用多重继承可以避免:①在基类放接口函数②用RTTI将父类的指针转换成派生类的

调用多态性,必须要用虚析构函数

4.  模拟抽象类

抽象类的作用:

为了实现一个统一的指针,我们可以定义一个类,再由这个类派生出父类和母类

5.  纯虚函数与抽象类

☆抽象类起到接口的作用,便于父类指针调用子类的对象

virtual viod A()=0;包含一个或者多个纯虚函数的类叫抽象类;

☆纯虚函数只起到接口的作用,要且必须在子类中重新定义

纯虚基类只能申明抽象类的指针,不能开辟抽象类的空间

6.  抽象类实例

7.  复杂的抽象结构

8.  慎用多重继承

在用单一继承可以实现的情况下不要使用多重继承

  1 #include <iostream>
  2 using namespace std;
  3 class animal
  4 {
  5 public:
  6     animal(int);
  7     virtual ~animal(){cout<<"析构动物..
";}
  8     virtual int getage() {return itsage;}
  9     virtual void sleep()=0;                //声明6个纯虚函数
 10     virtual void eat()=0;
 11     virtual void propagate()=0;
 12     virtual void move()=0;
 13     virtual void body()=0;
 14     virtual void show()=0;
 15 private:
 16     int itsage;
 17 };
 18 animal::animal(int age):itsage(age) 
 19 {
 20     cout<<"创建动物...
";
 21 }
 22 class Mammalia:public animal         //派生了另一个抽象类哺乳动物类
 23 {
 24 public:
 25     Mammalia(int age):animal(age){cout<<"创建哺乳类...
";}    //子类在构造自己的同时也要构造基类部分 
 26     virtual ~Mammalia(){cout<<"析构哺乳类...
";}    
 27     virtual void propagate(){cout<<"哺乳类是胎生动物,通过胚胎来繁殖后代。
";}
 28     //该类仅仅覆盖了基类的繁殖方法propagate()
 29 };
 30 class bird:public animal             //鸟类将动物类的6个纯虚函数全部覆盖,因此该类不是抽象类
 31 {
 32 public:
 33     //子类在构造自己的同时也要构造基类部分
 34     bird(int age):animal(age){cout<<"创建鸟类...
";}
 35     virtual ~bird(){cout<<"析构鸟类...
";}
 36     virtual void sleep(){cout<<"鸟类喜欢站着睡觉。
";}
 37     virtual void eat(){cout<<"极个别鸟类吃肉,其他都是吃素。
";}
 38     virtual void propagate(){cout<<"鸟类是卵生动物,通过排卵来繁殖后代.";}
 39     virtual void move(){cout<<"鸟类可以飞...
";}
 40     virtual void body(){cout<<"鸟类体表被覆羽毛,前肢变为翼!";}
 41     virtual void show(){cout<<"鸟类的一般寿命为:"<<getage()<<endl;}
 42 };
 43 class human:public Mammalia         //人类从抽象类--哺乳动物类派生而来
 44 {
 45 public:
 46     human(int age):Mammalia(age){cout<<"创建人类...
";}     //子类在构造自己的同时也要构造基类部分
 47     virtual ~human(){cout<<"析构人类...
";}
 48     //由于基类是个抽象类,因此如果要使该类起作用,那么就要将5个纯虚函数全部覆盖,这里覆盖了6个
 49     virtual void body(){cout<<"人类体表无毛...
";}
 50     virtual void sleep(){cout<<"人类睡觉也很讲究,要在床上睡觉。
";}
 51     virtual void eat(){cout<<"人类吃饭很讲究,不吃生食。
";}
 52     virtual void move(){cout<<"人类靠两条腿走路。
";}
 53     virtual void propagate(){cout<<"人类通过胚胎繁殖后代.
";}
 54     virtual void show(){cout<<"人类的一般寿命为:"<<getage()<<endl;}
 55 };
 56 class pig:public Mammalia            //猪类也是从抽象类--哺乳动物类派生而来
 57 {
 58 public:
 59     //子类在构造自己的同时也要构造基类部分
 60     pig(int age):Mammalia(age){cout<<"创建猪类...
";}
 61     virtual ~pig(){cout<<"析构猪类...
";}
 62     //这里也将抽象类Mammalia的6个方法全部覆盖
 63     virtual void body(){cout<<"猪体表被毛...
";}
 64     virtual void sleep(){cout<<"猪喜欢在烂泥里睡觉。
";}
 65     virtual void eat(){cout<<"猪虽然嘴谗,但是吃饭却不讲究。
";}
 66     virtual void move(){cout<<"猪用四肢走路。
";}
 67     virtual void propagate(){cout<<"猪类也通过胚胎来繁殖后代.
";}
 68     virtual void show(){cout<<"猪类因为要被人宰了吃,所以一般寿命为:"<<getage()<<"年。"<<endl;}
 69 };
 70 int main()
 71 {
 72     animal* ph=0;                       //声明一个指向动物类的指针ph
 73     int choice=0;                     //定义一个选择变量choice,并将其值赋为0,假如不赋值,那么后面的if语句将无法对其进行判断。
 74     bool quit=false;                //声明一个布尔变量quit,将其值赋为false
 75     while(choice<4)                 //假如输入的数值小于4,循环开始
 76     {
 77         choice=0;                     //由于switch无法对字符进行判断,所以每循环一次后要将choice的值归0,否则的话上一次输入的字符会在witch语句块中进行无休止的重复检测
 78         cout<<"(1)猪类(2)人类(3)鸟类(0)退出:";
 79         cin>>choice;
 80         switch(choice)
 81         {
 82         case 1:ph=new pig(1);         //选择1,创建猪类对象,并初始化猪类的私有变量itsage的值
 83             break;
 84         case 2:ph=new human(80);       //选择2,创建人类对象,并初始化人类的私有变量itsage的值
 85             break;
 86         case 3:ph=new bird(50);          //选择3,创建鸟类对象,并初始化鸟类的私有变量itsage的值
 87             break;                          //由于哺乳动物是个抽象类,不能实例化对象,因此没有设置该类的选项
 88         default:quit=true;              //假如选择了其他,那么默认将quit的值赋为true
 89             break;
 90         }
 91         if(quit)                            //假如quit的值为真
 92             break;                             //退出while循环
 93         ph->show();                        //用ph指针访问虚函数show,要注意,这里的show()不再是纯虚函数
 94         ph->eat();                        //用ph指针访问虚函数eat
 95         ph->propagate();                  //用ph指针访问虚函数propagate
 96         ph->move();                       //用ph指针访问虚函数move
 97         ph->sleep();                     //用ph指针访问虚函数sleep
 98         ph->body();                      //用ph指针访问虚函数body
 99         cout<<"
";
100     }
101     return 0;
102 }

本章总结:

/** ************重载,重写(覆盖),隐藏的识别*************
重载:如何调用取决于参数
覆盖:如何调用取决于object(有virtual 同名 同参)
隐藏:如何调用取决于pointer
a、编译时多态性:通过重载函数实现 
b、运行时多态性:通过虚函数实现。
包含纯虚函数(virtual void funtion()=0 )的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。
小结:1、有virtual才可能发生多态现象
2、不发生多态(无virtual)调用就按原类型调用
*/

#include<iostream>
using namespace std;

class Base
{
public:
    virtual void f(float x)
    {
        cout<<"Base::f(float)"<< x <<endl;
    }
    void g(float x)
    {
        cout<<"Base::g(float)"<< x <<endl;
    }
    void h(float x)
    {
        cout<<"Base::h(float)"<< x <<endl;
    }
private:
    float x;
};
class Derived : public Base
{
public:
    virtual void f(float x)
    {
        cout<<"Derived::f(float)"<< x <<endl;   //多态(覆盖)必须不同类
    }
    void g(int x)
    {
        cout<<"Derived::g(int)"<< x <<endl;     //隐藏(参数不同。此时,不论有无virtual关键字,
                                                //基类的函数将被隐藏(注意别与重载混淆)。)
    }                                            //重载必须要在同一个类中定义
    void h(float x)
    {
        cout<<"Derived::h(float)"<< x <<endl;   //隐藏(参数也相同,但是基类函数没有virtual关键字。
    }                                            //此时,基类的函数被隐藏(注意别与覆盖混淆)。)

};
int main(void)
{
    Derived d;
    Base *pb = &d;
    Derived *pd = &d;
    // Good : behavior depends solely on type of the object
    pb->f(3.14f);   // Derived::f(float) 3.14
    pd->f(3.14f);   // Derived::f(float) 3.14

    // Bad : behavior depends on type of the pointer
    pb->g(3.14f);   // Base::g(float)  3.14
    pd->g(3.14f);   // Derived::g(int) 3 

    // Bad : behavior depends on type of the pointer
    pb->h(3.14f);   // Base::h(float) 3.14
    pd->h(3.14f);   // Derived::h(float) 3.14
    return 0;
}
原文地址:https://www.cnblogs.com/zenseven/p/3794213.html