多态的意义(四十二)

        我们之前学习了函数重写,它便是在父类中被重写的函数依然会继承给子类,那么子类中重写的函数将覆盖父类中的函数,通过作用域分辨符(::)可以访问到父类中的函数。最后在全局函数中通过传递父类的指针,传递进去的是子类对象,竟然调用的不是子类中的成员函数。

        下来我们就先来介绍一个概念,那便是 C++ 中的三大特性之一多态。我们在面向对象中期望的行为是:根据实际的对象类型来自动判断调用的重写函数。父类指针(引用)指向,如果是父类对象则调用父类中定义的函数,如果是子类对象则调用子类中定义的重写函数。

        面向对象中的多态的概念:根据实际的对象类型决定函数调用的具体目标,同样的调用语句在实际运行时有多种不同的表现形态。如下

图片.png

        C++ 语言是直接支持多态的概念,它是通过使用 virtual 关键字对多态进行支持被 virtual 声明的函数被重写后具有多态性为,被 virtual 声明的函数叫做虚函数。 下来我们就对上节博客的最后一个示例程序进行改写

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    virtual void print()
    {
        cout << "I'm Parent!" << endl;
    }
};

class Child : public Parent
{
public:
    void print()
    {
        cout << "I'm Child!" << endl;
    }
};

void how_to_print(Parent* p)
{
    p->print();
}

int main()
{
    Parent p;
    Child c;
    
    p.print();         // I'm Parent.
    c.print();         // I'm Child.
    
    cout << endl;
    
    how_to_print(&p);  // I'm Parent.
    how_to_print(&c);  // I'm Child.
    
    return 0;
}

        我们在父类中的成员函数前用 virtual 关键字进行修饰,表明它是虚函数。那么在子类中的重写的函数前就不用进行 virtual 的声明了,因为它继承过来就是虚函数了。我们来编译看看效果

图片.png

        我们看到已经运行处我们所想要的结果了。那么多态存在的意义是什么呢?1、在程序运行过程中展现出动态的特性;2、函数重写必须多态实现,否则就没有意义;3、多态是面向对象组件化程序设计的基础特性。

        下来我们介绍两个新概念:静态联编和动态联编静态联编指的是在程序编译期间就能确定具体的函数调用,如函数重载动态联编则指的是在程序实际运行后才能确定具体的函数调用,如函数重写。下来我们通过一个示例代码来进行说明

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    virtual void func()
    {
        cout << "void func()" << endl;
    }
    
    virtual void func(int i)
    {
        cout << "void func(int i) : " << i << endl;
    }
    
    virtual void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
    }
};

class Child : public Parent
{
public:
    void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << i + j << endl;
    }
    
    void func(int x, int y, int z)
    {
        cout << "void func(int x, int y, int z) : " << x + y + z << endl;
    }
};

void run(Parent* p)
{
    p->func(1, 2);
}

int main()
{
    Parent p;
    
    p.func();      // 静态联编
    p.func(1);     // 静态联编
    p.func(1, 2);  // 静态联编
    
    cout << endl;
    
    Child c;
    
    c.func(1, 2);  // 静态联编
    
    cout << endl;
    
    run(&p);       // 动态联编
    run(&c);       // 动态联编
    
    return 0;
}

        我们这个函数通过虚函数来重写 func 函数。那么父类对象 p 所调用的三种重载函数都属于静态联编,因为它们都会在编译期间就会确定。接下来的子类对象 c 调用的 func 函数也属于静态联编,因为它在编译期间也直接就确定了。下面的全局函数 run 通过传递指针只能是在程序实际运行之后才能确定具体是父类的成员函数还是子类的成员函数的调用。

        下来我们为了更加形象的理解多态,便通过程序来描述一段江湖传说(唐长老之原创)。话说江湖上出现了一个大魔头,自古正邪不两立。以正派为首的名剑山庄肯定要去铲除这个大魔头,在名剑山庄的有个非常出名的剑谱,叫八剑齐飞。也就说要是能练成这招,便能同时召唤出八把名剑,同时的刺向敌人,360°无死角进攻,8 点杀伤力。那么大魔头肯定早就知道了这招,他便苦练神功,终于练成无名神功,有 10 点杀伤力。那么老庄主便和大魔头相约决战,两个人各自同时使出了自己的绝招,结果便是老庄主不敌,一战身亡了。那么少庄主便肯定要为父报 仇哈,便苦练八剑齐飞。但是他也知道这招根本就打不过大魔头,于是在机缘巧合之下学的一招:左右互搏之术。少庄主便结合这招使出了八剑齐飞,结果便是能同时召唤出 16 把名剑,将杀伤力提高到了 16。少庄主在学成之后,便去挑战大魔头。两人开打时,大魔头笑哭来,心想你老爹就用这招被我搞死了,你还用这招真是找死哈。没想到少庄主在使完一招之后,紧接着又使出一招。大魔头最终不敌,终将战死。少庄主便为父报 仇了,天下又重回安宁。下来我们就用程序来描述这段江湖传说

#include <iostream>
#include <string>

using namespace std;

class Boss
{
public:
    int fight()
    {
        int ret = 10;
        
        cout << "Boss::fight() : " << ret << endl;
        
        return ret;
    }
};

class Master
{
public:
    virtual int eightSwordKill()
    {
        int ret = 8;
        
        cout << "Mater::eightSwordKill() : " << ret << endl;
        
        return ret;
    }
};

class NewMaster : public Master
{
public:
    int eightSwordKill()
    {
        int ret = Master::eightSwordKill() * 2;
        
        cout << "NewMaster::eightSwordKill() : " << ret << endl;
        
        return ret;
    }
};

void filed_pk(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()
{
    Master master;
    Boss boss;
    
    cout << "Master VS Boss" << endl;
    
    filed_pk(&master, &boss);
    
    cout << endl;
    
    NewMaster newMaster;
    
    filed_pk(&newMaster, &boss);
    
    return 0;
}

        编译结果如下

图片.png

        此时,对于多态是不是就清晰了很多了呀。通过对多态的学习,总结如下:1、函数重写只可能发生在父类与子类之间;2、根据实际对象的类型确定调用的具体函数;3、virtual 关键字是 C++ 中支持多态的唯一方式;4、被重写的虚函数可表现出多态的特性。


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

猜你喜欢

转载自blog.51cto.com/12810168/2121422