4.1 KiB
4.1 KiB
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 类,扩充其“姓名”和“性别”属性,编写“学生”类继承于“人类”,扩充其“成绩”属性,基于这些类,实现学生成绩录入和显示系统。