C/C++|继承——类的三大特性之二


前言

本文简单介绍C++三大特性之一——继承,并通过简单代码进行说明。


一、类继承是什么 & 为什么要继承?

在大型项目中,使用已经经过测试的代码可以节省时间,同时有助于避免在程序中引入新的bug。尽管C++可以像C语言对函数重载来提供可重用的代码,但这需要重新编写代码。

所以C++的类提供了更高层次的代码重用性,叫做类继承,即:从已有的类派生出新的类,而派生类继承了原有类(基类)的特征,包括数据成员、成员函数等,同时派生类拥有不同于基类的特征。

继承有三种方式:公有继承(public)、保护继承(protected)和私有继承(private)。本文主要从公有继承的角度思考。

二、代码示例

0.派生类需要在继承特性中添加的东西

  • 派生类需要自己的构造函数
  • 派生类可以根据需要添加额外的数据成员和成员函数

1.基类和派生类的声明

代码如下(示例):

#ifndef TABTENN1_H_
#define TABTENN1_H_

#include <string>

//simple base class
class TableTennisPlayer{
private:
    std::string m_FirstName;
    std::string m_LastName;
    bool m_HasTable;
public:
    TableTennisPlayer(const std::string & fn = "none", 
                      const std::string & ln = "none", bool ht = false);
    void Name()const;
    bool HasTable() const { return m_HasTable; };
    void ResetTable(bool v) { m_HasTable = v; };   
};

//simple derived class
class RatedPlayer : public TableTennisPlayer{
private:
    unsigned int m_Rating;
public:
	RatedPlayer(unsigned int r, const TableTennisPlayer & fn,
        const std::string & ln, bool ht);
    RatedPlayer(unsigned int r = 0, const std::string & fn = "none",
                const std::string & ln = "none", bool ht = false);
    RatedPlayer(unsigned int r, const TableTennisPlayer & tp);
    unsigned int Rating() const { return m_Rating; }
    void ResetRating(unsigned int r) { m_Rating = r; }
};

#endif

2.类方法的定义

代码如下(示例):

#include "tabtenn1.h"
#include <iostream>

TableTennisPlayer::TableTennisPlayer(const std::string & fn, const std::string & ln,
                   bool ht) : m_FirstName(fn), m_LastName(ln), m_HasTable(ht) {}

void TableTennisPlayer::Name()const{
    std::cout << m_LastName << ", " << m_FirstName;
}

// RatedPlayer methods
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & fn,
        const std::string & ln, bool ht){
    m_Rating = r;
}

RatedPlayer::RatedPlayer(unsigned int r, const std::string & fn,
            const std::string & ln, bool ht) : TableTennisPlayer(fn, ln, ht){
    m_Rating = r;
}

RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
    : TableTennisPlayer(tp), m_Rating(r)//使用隐式拷贝构造函数
{

}

3. 构造函数

派生类不能直接访问基类的私有成员,必须通过基类的成员函数进行访问。

在创建派生类对象时,必须先创建基类对象。因为派生类对象的构造函数无法直接访问基类TableTennisPlayer的私有成员变量,它必须使用基类构造函数,C++中使用成员初始化列表来完成这个工作,如下面代码所示:

RatedPlayer::RatedPlayer(unsigned int r, const std::string & fn,
            const std::string & ln, bool ht) : TableTennisPlayer(fn, ln, ht){
    m_Rating = r;
}

如果省略成员初始化列表,编译器降提供默认的基类构造函数,如下:

RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & fn,
        const std::string & ln, bool ht) // : TableTennisPlayer()
{
    m_Rating = r;
}

总结下派生类的构造函数的要点:

  • 首先创建基类对象
  • 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
  • 派生类构造函数应初始化派生类新增的数据成员

释放对象的顺序与创建对象的顺序相反,即先执行派生类的析构函数,然后自动调用基类的析构函数。

三、派生类和基类之间的特殊关系

1. 派生类对象可以使用基类的公有或保护方法(派生类对象无法直接使用基类的私有方法)

RatedPlayer rp1(1140, "Mallory", "Duck", true);
rp1.Name();	//输出:Duck, Mallory

Name()方法是基类TableTennisPlayer的公有成员方法,基类TableTennisPlayer被派生类RatedPlayer继承,派生类对象rp1可以调用基类TableTennisPlayerName()方法。

2. 基类指针可以在不进行显示类型转换的情况下指向派生类对象

RatedPlayer rp1(1140, "Mallory", "Duck", true);
TableTennisPlayer* p = &rp1;
p->Name();	//输出:Duck, Mallory

3. 基类引用可以在不进行显式类型转换的情况下应用派生类对象

RatedPlayer rp1(1140, "Mallory", "Duck", true);
TableTennisPlayer & rt = rp1;
rt.Name();	//输出:Duck, Mallory

4. 将基类对象赋给派生类引用或指针时,编译器会报错

TableTennisPlayer player("Betsy", "bloop", true);
RatedPlayer & rp1 = player;//error: non-const lvalue reference to type 'RatedPlayer' cannot bind to a value of unrelated type 'TableTennisPlayer
RatedPlayer* rp2 = player;//error: no viable conversion from 'TableTennisPlayer' to 'RatedPlayer *'

总结

本文主要介绍了C++的三大特性之二——继承,并通过简单的代码进行展示。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值