HaveFunWithEmbeddedSystem/Chapter2_C_与_C++/2.10_类、继承和多态.md
chen.yang 9633d5ea91 调整文档结构.
Signed-off-by: chen.yang <chen.yang@yuzhen-iot.com>
2022-03-28 11:47:19 +08:00

4.1 KiB
Raw Blame History

2.10 类、继承和多态

2.10.1 类和多态

在 C++ 中,我们通过如下方法定义和实现一个类:

/**
 * @file    Animal.hpp
 */
#pragma once        // 只被编译一次.

#include <string>       // 使用 C++ 的 string 类.
using namespace std;    // 使用 std 命名空间.

class Animal
{
public:
    Animal();           // 构造函数.
    virtual ~Animal();  // 析构函数.
    virtual void Sound(void);
    void Feed(double rate) { Growth(rate); }

    char Color[3];                      // 属性.
    double Height;
    double Width;

protected:
    virtual void Growth(void);          // 方法.
    virtual void Growth(double rate);

    string Sounds;

private:
    void MakeSounds(void);

    string Shape;

};

可以看到Animal 类中有两个参数不同的 Growth 方法,这被称作多态,在使用 Animal 对象时,依据调用 Growth 方法时传入的参数个数和类型来判断具体使用哪个 Growth 方法。Animal 类的实现为:

/**
 * @file    Animal.cpp
 */
#include "Animal.hpp"
#include <iostream>

Animal::Animal()
{
    this->Height = 8.5;
    this->Sounds = "Bebe...";
}

Animal::~Animal()
{
    cout<<"Dead."<<endl;
}

void Animal::Sound(void)
{
    this->MakeSounds();
}

void Animal::Growth(void)
{
    Height += 0.2;
    Width += 0.1;
}

void Animal::Growth(double rate)
{
    Height *= (1+rate);
    Width *= (1+rate);
}

void Animal::MakeSounds(void)
{
    cout<<Sounds<<endl;
}

在这个类里,我们实现了公有,保护和私有属性和方法。提供了虚方法以允许子类对虚方法进行覆盖。还提供了 Growth 方法的多态实现。

2.10.2 继承和多态

动物这个类下有多个子类,比如说可爱的小狗,它继承于 Animal 这个类,如下:

/**
 * @file    Puppy.hpp
 */
#pragma once

#include "Animal.hpp"

class Puppy : public Animal
{
public:
    Puppy();
    virtual ~Puppy();

protected:
    virtual void Growth(void);
    virtual void Growth(double rate);
    virtual void Growth(double height, double width);

};

可以看到Puppy 类覆盖了 Animal 类中两个 Growth 方法,并且通过多态扩充了一个 Growth 方法。看下 Puppy 类的实现:

/**
 * @file    Puppy.cpp
 */
#include "Puppy.hpp"
#include <iostream>

Puppy::Puppy()
{
    this->Height = 0.6;
    this->Sounds = "Wang!Wang!Wang!";
}

Puppy::~Puppy()
{
    cout<<"Puppy Dead. Wu...Wu...T_T..."<<endl;
}

void Puppy::Growth(void)
{
    Height += 0.03;
    Width += 0.03;
}

void Puppy::Growth(double rate)
{
    Height = Height*(1+rate)+0.5;
    Width = Width*(1+rate)+0.5;
}

void Puppy::Growth(double height, double width)
{
    Height = height;
    Width = width;
}

在 Puppy 类的构造函数中,将 Sounds 属性赋值成了 "Wang!Wang!Wang!" 的狗叫声,当我们调用 Puppy 对象的 Sound 方法时,就会出现 "Wang!Wang!Wang!" 而不是 Animal 中的 "Bebe..."。这是因为,在初始化 Puppy 对象时,先调用了 Animal 的默认构造函数,对来自父类的属性进行了初始化之后,又调用了 Puppy 的构造函数,在 Puppy 的构造函数中,再次初始化 Sounds 属性,覆盖了原有的 Animal 中的赋值。

下面我们看看在 main 函数中如何调用这两个类:

/**
 * @file    main.cpp
 */
#include <Animal.hpp>
#include <Puppy.hpp>

int main() {
    Animal* animal;
    animal = new Animal();
    animal->Color[1] = 255;
    animal->Sound();        // 输出 Bebe...
    delete animal;          // 输出 Dead.

    Puppy* puppy;
    puppy = new Puppy();
    puppy->Color[0] = 255;
    puppy->Feed(0.2);       // 调用 Animal 类中的 void Growth(double rate) 方法.
    puppy->Sound();           // 输出 Wang!Wang!Wang!
    delete puppy;             // 输出 Puppy Dead. Wu...Wu...T_T... 和 Dead.
    return 0;
}

通过 puppy 调用 Growth 方法那句,更能体现类抽象的实质。

练习

编写“人类”继承于 Animal 类,扩充其“姓名”和“性别”属性,编写“学生”类继承于“人类”,扩充其“成绩”属性,基于这些类,实现学生成绩录入和显示系统。