阅读 781

【重学面向对象】篇一:到底什么是多态?| 七日打卡

前言

接触Java这么久,我们一定都知道,作为面向对象阵营的典型代表,封装、继承、多态 (其实还应该包含抽象)是其三大基本特征

但是到底什么是多态,它的底层又是如何实现的,又有什么优缺点,可能就没那么容易说清楚

今天我们从它的近亲 C++ 语言来重新审视下,到底什么是多态

下面,正文开始!

什么是多态

多态(Polymorphism)是面向对象的三大基本特征之一,多态是在 面向对象 的语境下,将数据模型封装为类对象,而对象之间又含有继承关系,一个基类(base class)可以有不同的派生类(derived class),不同的派生类有各自不同的行为,这种用法就叫做多态

多态是怎么实现的

从语言层面讲,多态依赖于面向对象的继承(implement)和函数的重写来实现,一个基类可以派生出不同的子类对象,不同的子类对象可以有各自不同的行为,正所谓,龙生九子,凤育九雏

从编译器角度讲,多态依赖于不同的派生类有相同的 指针类型 ,所以创建一个类的时候,可以指向他的基类

进一步讲,派生类和基类的函数会有一个虚函数表(vtable)来存储他们的指针,以达到子类能继承和调用基类成员的功能,如下图所示

image.png

图片引自 JerryFu 的博客 >>> www.cnblogs.com/jerry-fuyi/…

多态有什么好处

我们经常强调要 面向接口编程/面向父类编程 ,一个定义良好的基类,基本上是不需要修改的,需要修改的只有不同派生类的不同行为而已

因此,当我们在调用层面需要改变具体的行为时,可以修改派生类或者重新创建一个派生类,来满足业务需求

而调用层只需要将原来的派生类改为我们重新创建的派生类即可,达到以最少的代码改动实现对应需求的效果

多态有什么缺点

多态的缺点在于,其针对真实世界 对象 的模拟,在基类定义不准确时,会产生行为不一致的后果

比如我们定义一个基类 Bird ,根据常识,飞行是鸟的基本能力,所以我们定义了一个函数 fly()

不同的鸟类可能又有各自的不同的行为,我们再定义一个派生类 Ostrich (鸵鸟), 但是鸵鸟是不会飞的,这就导致了派生类与基类行为不一致的后果

这个问题是多态没有办法解决的,所以必须通过其他形式给多态打个 补丁 ,即通过函数重写,来改变基类方法,实现派生类自己的行为;但函数重写没有办法解决的是在调用层进行调用时,行为不一致导致的业务逻辑混乱,这时则需要更多的代码去修改调用层

所以当我们定义基类的时候,尽量要想清楚,哪些函数是公共的,需要放在基类中的,以减少这种情况的发生

多态的写法

上面讲了很多理论,下面我们通过代码来看一看,C++中多态的写法

源码请查看 >>> github.com/liyilin-jac…

话说上帝创造人类的时候,也是使用了面向对象的思想

上帝先定义了一个基类Human ,Human中有定义了虚函数 pee()walk() ,交给派生类去实现 , 由于所有人类都会爬行,所以这里直接实现了爬行的函数

#include <string>
#include <vector>

using namespace std;

class Human{
    
public:
    string name = "";
    int age = 1;
    
    Human(){
    };
    
    
    virtual void pee(){
        
    };
    
    virtual void walk(){
        
    };
    void crawl(){
        printf("%s crawing...\n",name.c_str());
    };
    
    
};
复制代码

上帝又定义一个派生类Baby, 由于baby太小,还没学会走路,所以baby的 walk() 函数中进行了判断,大于等于两岁才可 walk()

class Baby:public Human{
    
public:
    Baby(){};
    
    Baby(string name){
        this->name = name;
    };
    
    ~Baby(){
        delete &name;
        delete &age;
    };
    
    void pee() override{
        if ((age)<3) {
            printf("%s pee with it's parents help...\n",name.c_str());
        }else{
            printf("%s pee by itself...\n",name.c_str());
        }
    }
    
    void walk() override{
        if ((age)<2) {
            printf("%s can't walk yet...\n",name.c_str());
        }else{
            printf("%s walking...\n",name.c_str());
        }
    }
};
复制代码

上帝又创造了一个派生类Man ,该类中也根据实际场景实现了 pee()walk 函数

class Man:public Human{
public:
    Man();
    
    Man(string name,int age){
        this->name = name;
        this->age = age;
    }
    
    void walk() override{
        printf("%s walking...\n",name.c_str());
    }
    
    void pee() override{
        printf("%s pee by standing...\n",name.c_str());
    }
};
复制代码

有了Man,再创造一个Woman吧!这样人类就可以自己繁衍后代了

class Woman:public Human{
public:

    vector<Baby*> *babys = new vector<Baby*>(10);
    
    Woman();
    
    Woman(string name,int age){
        this->name = name;
        this->age = age;
    }

    Baby* haveABaby(Man* man,string name);

    void walk() override{
        printf("%s walking...\n",name.c_str());
    }
    void pee() override{
        printf("%s pee by sitting...\n",name.c_str());
    }
};

Baby* Woman::haveABaby(Man* man,string name){
    Baby *baby = NULL;
    if (man) {
        baby = new Baby(name);
        babys->push_back(baby);
    }
    return baby;
}
复制代码

人类创造完了,该让他们干活了,现在开启人类世界(C++的main函数)

void createWorld();

int main(int argc, const char * argv[]) {
    createWorld();
    return 0;
}

void createWorld(){
    //create Adam
    Human* Adam = new Man("Adam",18);
    Adam->walk();
    Adam->pee();
    //create Eve
    Human* Eve = new Woman("Eve",16);
    Eve->walk();
    Eve->pee();
    //Adam meet Eve
    //Eve have a baby Gain
    string name_gain = "Gain";
    Human *Gain =  ((Woman*)Eve)->haveABaby((Man*)Adam,name_gain);
    Gain->walk();
    Gain->pee();
    Gain->crawl();
    //Eve have a baby Abel
    string name_abel = "Abel";
    Human *Abel =  ((Woman*)Eve)->haveABaby((Man*)Adam,name_abel);
    Abel->age = 4;
    Abel->walk();
    Abel->pee();
    Abel->crawl();
}
复制代码

亚当和夏娃偷食禁果,生下了 GainAbel 两个孩子

自此,人类生生不息,繁衍至今

后记

以上是笔者最近学习C++的一点心得,如果你有不同的看法,或者心得体会,欢迎在评论中继续探讨

我是释然,我们下篇文章再见!

文章分类
Android
文章标签