• 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++中支持多态的唯一方式。

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

  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/free-1122/p/11336223.html
Copyright © 2020-2023  润新知