zoukankan      html  css  js  c++  java
  • C++--第17课

    第17课 - 继承与多态 - 上

    引入:    

    如果子类定义了与父类中原型相同的函数会发生什么?

    1. 函数重写

    在子类中定义与父类中原型相同的函数,函数重写只发生在父类与子类之间。

    父类中被重写的函数依然会继承给子类,默认情况下子类中重写的函数将隐藏父类中的函数,通过作用域分辨符::可以访问到父类中被隐藏的函数。

    #include <cstdlib>

    #include <iostream>

    using namespace std;

    class Parent

    {

    public:

        void print()

        {

            cout<<"I'm Parent..."<<endl;

        }

    };

    class Child : public Parent

    {

    public:

        void print()

        {

            cout<<"I'm Child..."<<endl;

        }

    };

    int main(int argc, char *argv[])

    {

        Child child;

        child.print();

        child.Parent::print();

        cout << "Press the enter key to continue ...";

        cin.get();

        return EXIT_SUCCESS;

    }

    运行结果:

    I'm Child...

    I'm Parent...

    当函数遇到兼容性问题的时候:

    #include <cstdlib>

    #include <iostream>

    using namespace std;

    class Parent

    {

    public:

        void print()

        {

            cout<<"I'm Parent..."<<endl;

        }

    };

    class Child : public Parent

    {

    public:

        void print()

        {

            cout<<"I'm Child..."<<endl;

        }

    };

    void howToPrint(Parent* p)

    {

        p->print();

    }

    void run()

    {

        Child child;

        Parent* pp = &child;   //赋值兼容性原则,当使用父类的时候都能用子类来代替。

        Parent& rp = child; //Parent类引用,应用Child类对象

        child.print();

        pp->print();  //指针打印

        rp.print();   //引用的打印

    }

    int main(int argc, char *argv[])

    {

        run();

        cout << "Press the enter key to continue ...";

        cin.get();

        return EXIT_SUCCESS;

    }

    运行结果:

    I'm Child...

    I'm Parent...

    I'm Parent...

    分析:

    pp执行child,rp引用child。但是后面的两条语句都没有打印I'm Child...,而是I'm Parent...。

    l  问题所在:

    (1)      C++与C相同,是静态编译型语言。

    (2)      在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象。

    (3)      所以编译器认为父类指针指向的是父类对象(根据赋值兼容性原则,这个假设合理)。

    (4)      由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象。

    (5)      从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。

    对于下面的程序:

    void howToPrint(Parent* p)

    {

    p->print();

    }

    在编译这个函数的时候,编译器不可能知道指针 p 究竟指向了什么。但是编译器没有理由报错。于是,编译器认为最安全的做法是编译到父类的print函数,因为父类和子类肯定都有相同的print函数。

    2. 程序—函数重写实例(江湖恩怨)

    #include <cstdlib>

    #include <iostream>

    using namespace std;

    /*大boss挑战庄主与少庄主*/

    class Boss   //大boss

    {

    private:

        static Boss* cInstance;  //单例模式,就一个 .声明

        Boss()

        {

        }

    public:

        static Boss* GetInstance()

        {

            if( cInstance == NULL )

            {

                 cInstance = new Boss();

            }   

            return cInstance;

        }

        int fight()

        {

            cout<<"Boss::fight()"<<endl;   //大boss出招

            return 10;

        }

    };

    Boss* Boss::cInstance = NULL;

    //静态变量上面是声明,下面是定义,否则没有意思。

    class Master   //庄主

    {

    public:

        virtual int eightSwordKill() //八剑齐飞

              

        {

            cout<<"Master::eightSwordKill()"<<endl;

            return 8;

        }

    };

    class NewMaster : public Master //少庄主

    {

    public:

        virtual int eightSwordKill()

        {

            cout<<"NewMaster::eightSwordKill()"<<endl;

            return Master::eightSwordKill() * 2;

        }

    };

    void fieldPK(Master* master, Boss* boss)

    {

        int k = master->eightSwordKill();

        int b = boss->fight();

        if( k < b )

        {

            cout<<"Master is killed..."<<endl;

        }

        else

        {

            cout<<"Boss is killed..."<<endl;

        }

    }

    int main(int argc, char *argv[])

    {

        Boss* boss = Boss::GetInstance();

        cout<<"Master vs Boss"<<endl;

        Master master;

        fieldPK(&master, boss);

        cout<<"New Master vs Boss"<<endl;

        NewMaster newMaster;

        fieldPK(&newMaster, boss);

        cout << "Press the enter key to continue ...";

        cin.get();

        return EXIT_SUCCESS;

    }

    运行结果:

    Master vs Boss

    Master::eightSwordKill()

    Boss::fight()

    Master is killed...

    New Master vs Boss

    NewMaster::eightSwordKill()

    Master::eightSwordKill()

    Boss::fight()

    Boss is killed...

    3. 多态的本质

    l  面向对象的新需求

    根据实际的对象类型来判断重写函数的调用。

    如果父类指针指向的是父类对象则调用父类中定义的函数。

    如果父类指针指向的是子类对象则调用子类中定义的重写函数。

    l  面向对象中的多态

    根据实际的对象类型决定函数调用语句的具体调用目标。

    多态:同样的调用语句有多种不同的表现形态

    l  C++中的多态支持

    C++中通过virtual关键字对多态进行支持。

    使用virtual声明的函数被重写后即可展现多态特性。

    这就是虚函数。虚函数的特点是,不再只是根据指针类型来判定要使用的函数,而是根据指针所指的内容来判断将要引用的函数。

    小结

    函数重写是面向对象中很可能发生的情形。

    函数重写只可能发生在父类与子类之间。

    需要根据实际对象的类型确定调用的具体函数。

    virtual关键字是C++中支持多态的唯一方式。

    被重写的虚函数即可表现出多态的特性。

  • 相关阅读:
    CentOS忘记密码修改方案以及centos卡在开机登录界面,命令失效的解决方法
    音乐制作模块
    CentOS7安装RabbitMQ,并设置远程访问
    设计模式
    排序
    经典排序算法及python实现
    scrapy初步解析源码即深度使用
    JAVA----线程初级
    Python随笔--魔法方法(析构与构造)
    Python随笔--对象
  • 原文地址:https://www.cnblogs.com/free-1122/p/11336223.html
Copyright © 2011-2022 走看看