• 第十六章 多态性


    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;
    }
  • 相关阅读:
    团购倒计时
    折半查找
    比较函数
    行为驱动开发: Cucumber的目录结构和执行过程 (转载)
    ruby 方法查找 and执行方法
    Rubyinstance_variable_get(@xx)
    Ruby 模块
    散列
    ruby webdriver 启动firefox driver时,加载firebug的扩展
    git使用简介
  • 原文地址:https://www.cnblogs.com/zenseven/p/3794213.html
Copyright © 2020-2023  润新知