C++基础教程面向对象(学习笔记(44))

C ++中的基本继承

现在我们已经谈到了抽象意义上的继承,让我们来谈谈它是如何在C ++中使用的。

C ++中的继承发生在类之间。在继承(is-a)关系中,从中继承的类称为父类,基类,而执行继承的类称为子类,派生类或子类。
在这里插入图片描述

在上图中,Fruit是父类,Apple和Banana都是子类。
在这里插入图片描述

在此图中,Triangle既是子(也是Shape),也是父(右三角)。

子类从父级继承行为(成员函数)和属性(成员变量)(受一些访问限制的约束,我们将在以后的课程中介绍)。
这些变量和函数成为派生类的成员。
因为子类是完整的类,所以它们(当然)可以拥有自己的特定于该类的成员。我们马上就会看到一个例子。

一个Person

这是一个代表普通人的简单类:

#include <string>
 
class Person
{
public:
    std::string m_name;
    int m_age;
 
    Person(std::string name = "", int age = 0)
        : m_name(name), m_age(age)
    {
    }
 
    std::string getName() const { return m_name; }
    int getAge() const { return m_age; }
 
};

因为此Person类旨在表示一般人,所以我们只定义了对任何类型的人都通用的成员。每个人(不论性别,职业等)都有姓名和年龄,所以这些人都在这里代表。

请注意,在此示例中,我们已将所有变量和函数公开。这纯粹是为了让这些例子现在保持简单。通常我们会将变量设为私有。我们将在本章后面讨论访问控制以及它们如何与继承交互。

一个BaseballPlayer类

假设我们想编写一个程序来跟踪一些棒球运动员的信息。棒球运动员需要包含特定于棒球运动员的信息 - 例如,我们可能想要存储球员的击球率,以及他们击中的本垒打数量。

这是我们不完整的棒球运动员类:

class BaseballPlayer
{
public:
    double m_battingAverage;
    int m_homeRuns;
 
    BaseballPlayer(double battingAverage = 0.0, int homeRuns = 0)
       : m_battingAverage(battingAverage), m_homeRuns(homeRuns)
    {
    }
};

现在,我们还想跟踪棒球运动员的名字和年龄,我们已将这些信息作为Person类的一部分。

我们有三个选择如何为BaseballPlayer添加名称和年龄:
1)直接将名称和年龄添加到BaseballPlayer类作为成员。这可能是最糟糕的选择,因为我们正在复制Person类中已存在的代码。对Person的任何更新也必须在BaseballPlayer中进行。
2)使用组合添加Person作为BaseballPlayer的成员。但我们不得不问自己,“棒球运动员是否有人”?不,它没有。所以这不是正确的范例。
3)让BaseballPlayer从Person继承这些属性。请记住,继承代表了一种is-a关系。BaseballPlayer是一个人吗?是的。所以继承是一个很好的选择。

使BaseballPlayer成为派生类

要让BaseballPlayer继承我们的Person类,语法非常简单。在class BaseballPlayer声明之后,我们使用冒号,单词“public”,以及我们希望继承的类的名称。这称为公共继承。我们将在未来的课程中详细讨论公共继承的含义。

// BaseballPlayer公开继承Person
class BaseballPlayer : public Person
{
public:
    double m_battingAverage;
    int m_homeRuns;
 
    BaseballPlayer(double battingAverage = 0.0, int homeRuns = 0)
       : m_battingAverage(battingAverage), m_homeRuns(homeRuns)
    {
    }
};

使用派生图,我们的继承如下所示:

在这里插入图片描述

当BaseballPlayer继承自Person时,BaseballPlayer从Person获取成员函数和变量。此外,BaseballPlayer定义了两个自己的成员:m_battingAverage和m_homeRuns。这是有道理的,因为这些属性特定于BaseballPlayer,而不是任何Person。

因此,BaseballPlayer对象将有4个成员变量:来自BaseballPlayer的m_battingAverage和m_homeRuns,以及来自Person的m_name和m_age。

这很容易证明:

#include <iostream>
#include <string>
 
class Person
{
public:
    std::string m_name;
    int m_age;
 
    Person(std::string name = "", int age = 0)
        : m_name(name), m_age(age)
    {
    }
 
    std::string getName() const { return m_name; }
    int getAge() const { return m_age; }
 
};
 
// BaseballPlayer公开继承 Person
class BaseballPlayer : public Person
{
public:
    double m_battingAverage;
    int m_homeRuns;
 
    BaseballPlayer(double battingAverage = 0.0, int homeRuns = 0)
       : m_battingAverage(battingAverage), m_homeRuns(homeRuns)
    {
    }
};
 
int main()
{
    //创建一个新的BaseballPlayer对象
    BaseballPlayer joe;
    // 为它指定一个名称(我们可以直接执行此操作,因为m_name是公共的)
    joe.m_name = "Joe";
    // 打印出name
    std::cout << joe.getName() << '\n'; // use the getName() function we've acquired from the Person base class
 
    return 0;
}

其中打印的值:
Joe
可以编译和运行,因为Joe是一个BaseballPlayer,所有BaseballPlayer对象都有一个m_name成员变量和一个继承自Person类的getName()成员函数。

Employee派生类

现在让我们编写另一个也继承自Person的类。这一次,我们将编写一个Employee类。员工“是一个”人,所以使用继承是合适的:

// Employee公开继承Person
class Employee: public Person
{
public:
    double m_hourlySalary;
    long m_employeeID;
 
    Employee(double hourlySalary = 0.0, long employeeID = 0)
        : m_hourlySalary(hourlySalary), m_employeeID(employeeID)
    {
    }
 
    void printNameAndSalary() const
    {
        std::cout << m_name << ": " << m_hourlySalary << '\n';
    }
};

Employee从Person继承m_name和m_age(以及两个访问函数),并添加另外两个成员变量和一个自己的成员函数。请注意,printNameAndSalary()使用它所属的类(Employee :: m_hourlySalary)和父类(Person :: m_name)中的变量。

这给了我们一个如下所示的派生图:
在这里插入图片描述

请注意,Employee和BaseballPlayer没有任何直接关系,即使它们都从Person继承。

以下是使用Employee的完整示例:

#include <iostream>
#include <string>
 
class Person
{
public:
    std::string m_name;
    int m_age;
 
    std::string getName() const { return m_name; }
    int getAge() const { return m_age; }
 
    Person(std::string name = "", int age = 0)
        : m_name(name), m_age(age)
    {
    }
};
 
// Employee公开继承 Person
class Employee: public Person
{
public:
    double m_hourlySalary;
    long m_employeeID;
 
    Employee(double hourlySalary = 0.0, long employeeID = 0)
        : m_hourlySalary(hourlySalary), m_employeeID(employeeID)
    {
    }
 
    void printNameAndSalary() const
    {
        std::cout << m_name << ": " << m_hourlySalary << '\n';
    }
};
 
int main()
{
    Employee frank(20.25, 12345);
    frank.m_name = "Frank"; // 我们可以这样做,因为m_name是公共的
 
    frank.printNameAndSalary();
    
    return 0;
}

这打印:

Frank:20.25
继承链

可以从一个本身派生自另一个类的类继承。这样做没有什么值得注意或特别的 ,一切都按照上面的例子进行。

例如,让我们写一个Supervisor类。主管是员工,是一个人。我们已经编写了一个Employee类,所以让我们使用它作为派生Supervisor的基类:

class Supervisor: public Employee
{
public:
    // 这个Supervisor可以监督最多5名员工
    long m_overseesIDs[5];
 
    Supervisor()
    {
    }
 
};

现在我们的派生图如下所示:
在这里插入图片描述

所有Supervisor对象都从Employee和Person继承函数和变量,并添加自己的m_overseesIDs成员变量。

通过构造这样的继承链,我们可以创建一组可重用的类,这些类非常通用(在顶部)并且在每个继承级别逐渐变得更具体。

为什么这种继承有用?

继承自基类意味着我们不必在派生类中重新定义基类中的信息。我们通过继承自动接收基类的成员函数和成员变量,然后简单地添加我们想要的附加函数或成员变量。这不仅可以节省工作,还意味着如果我们更新或修改基类(例如添加新函数或修复错误),我们所有派生类都将自动继承更改!

例如,如果我们向Person添加了一个新功能,那么Employee和Supervisor都会自动获得对它的访问权限。如果我们向Employee添加了一个新变量,Supervisor也可以访问它。这使我们能够以简单,直观和低维护的方式构建新类!

Conclusion:

继承允许我们通过让其他类继承其成员来重用类。在未来的课程中,我们将继续探索其工作原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值