当前位置: 首页 > news >正文

C++进阶:继承

文章目录

  • 继承:
    • 父类(parent class),
  • 继承(inheritance)
    • 子类(child class)
    • 类层次
    • 继承链
  • 多继承(菱形继承)
    • 子类是怎么继承的?
    • 如何在初始化基类?
  • 访问控制
    • (1)第一组public继承:
    • (2)第一组protected继承:
    • (3) private继承

构建复杂类有两种方式, 一种是对象组合, 另一种是继承.

继承:

为什么要用到继承呢? 因为它更省事, 就好像是我们不用在去发现一遍牛顿定理, 而是直接使用它的理论就行.
继承也是如此, 在涉及第三方类的时候, 无需重新实现一遍, 只要继承它, 使用我们用到的功能就行, 这提高了开发效率.

继承在现实生活中也随处可见。当我们出生时,我们继承了父母的基因,并从他们身上获得了身体属性——但随后我们又在此基础上添加了自己的个性。

而父母就是基类, 我们就是子类, 继承关系可以表示为子类指向父类的一个箭头, 表示子类引用父类(而不是相反)
在这里插入图片描述

C++ 中的继承发生在类之间。在继承关系中,被继承的类称为父类、基类或超类,而进行继承的类称为子类、派生类或子类。

父类(parent class),

也叫基类(base class), 超类(superclass)
在这里插入图片描述

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

继承(inheritance)

现在让我们编写一个也继承自 Person 的类-------Employee 类。员工“就是”人,因此使用继承是合适的:
在这里插入图片描述

子类(child class)

也叫派生类, 或者衍生类(derived class),子类 (subclass)
在这里插入图片描述

class Employee:public Person
{
public:double m_hourlySalary{};long m_employyeID{};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 的完整示例:

#include <string_view>class Persion
{
public:std::string m_name{};int m_age{};Person(std::string_view name = " ", int age = 0):m_name{ name },m_age{ age }{}const std::string& getName() const { return m_name; }int getAge() const { return m_age; }
};class Employee:public Person
{
public:double m_hourlySalary{};long m_employyeID{};Employee(double hourlySalary = 0.0, long employeeID = 0):m_hourlySalary{ hourlySalary },m_employeeID{ employeeID }{}void printNameAndSalary() const{std::cout << m_name << ": " << m_hourlySalary << '\n';}
};

我们再编写一个Manager类, 继承Employee, 类层次表示为经理也是一个员工

#include <string>
#include <string_view>
#include <list>
class Persion
{
public:std::string m_name{};int m_age{};Person(std::string_view name = " ", int age = 0):m_name{ name },m_age{ age }{}const std::string& getName() const { return m_name; }int getAge() const { return m_age; }
};class Employee:public Person
{
public:double m_hourlySalary{};long m_employyeID{};Employee(double hourlySalary = 0.0, long employeeID = 0):m_hourlySalary{ hourlySalary },m_employeeID{ employeeID }{}void printNameAndSalary() const{std::cout << m_name << ": " << m_hourlySalary << '\n';}
};class Manager : public Employee
{list<Employee*> m_group{};// 所管理的员工short m_level{};          // 职称等级
};

继承链

在这里插入图片描述
像上面这种经理就是员工, 员工就是人, 从而形成了一条 “经理->员工->人” 的链, 叫做继承链.

所有 Manager 对象都继承了 Employee 和 Person 的函数和变量,并添加了自己的 m_group , m_level成员变量。
通过构建这样的继承链,我们可以创建一组可重用的类,这些类非常通用(在顶部),并且在每个继承级别上变得越来越具体。

多继承(菱形继承)

在涉及继承链中, 总会遇到一个金典的错误, 就是菱形继承;

在这里插入图片描述在调用name的时候,编译器不知道是从D->B->A过来的, 还是D->C->A过来的, 所以发生了二义性错误, 这种情况下只需加上限定类就可以了, 比如d.B::name 或者d.C::name都可以.

子类是怎么继承的?

首先,让我们介绍一些新的类,它们将帮助我们说明一些要点。

#include <iostream>
class Base
{
public:int m_id {};Base(int id=0):m_id {id}{}int getId() const { return m_id; }
};class Derived: public Base
{
public:double m_cost {};Derived(double cost=0.0):m_cost { cost }{}double getCost() const { return m_cost; }
};

在这里插入图片描述

那么我们会问到底继承了什么?

  • A, 是子类把基类的数据成员 和给自己拷贝一份吗?
    在这里插入图片描述

  • B,还是有两部分, 一部分为Base, 另一部分为Derived?

在这里插入图片描述
请选择你的猜想? 选择A 还是B?

int main()
{Derived d;return 0;
}

为了能够显示正确的过程, 我们在初始化器中加上可以辨别的输出语句, 来测试一下?

在这里插入图片描述

以下是子类实例化时实际发生的情况:

  • 为子类部分预留内存(足够用于基础部分和子类部分)
  • 调用适当的 Derived 构造函数
  • 首先使用适当的 Base 构造函数构造 Base 对象。如果没有指定基构造函数,则将使用默认构造函数。
  • 成员初始化列表初始化变量
  • 构造函数主体执行
  • 控制权返回给调用者

注意:在 Derived 构造函数执行任何实质性操作之前,Base 构造函数会先被调用。Base 构造函数设置对象的 Base 部分,然后控制权返回给 Derived 构造函数,最后 Derived 构造函数才能完成其工作
所以正确答案是B

如何在初始化基类?

在上面的子类Derived中我们想初始化基类Base, 该如何做呢?
尝试一下如下面这么做会怎么样?

 Derived(double cost=0.0, int id = 5)//// 错误!不能在派生类中直接初始化基类成员:m_cost { cost },m_id { id }{std::cout << "Derived\n";}

在这里插入图片描述
经了解, 类的构造函数初始化列表只能初始化:

  • 派生类自己的非静态数据成员
  • 直接基类(通过调用基类构造函数)

但我们如果这样写呢?

 Derived(double cost=0.0, int id = 5):m_cost { cost },Base { id }            // 回归注意内容: 它的初始化顺序为Base->Derived, 先有子类, 后有父类, 子类是建立在父类的基础上的{std::cout << "Derived\n";}

在这里插入图片描述
修改初始化顺序就可以了

 Derived(double cost=0.0, int id = 5):Base { id },m_cost { cost }{std::cout << "Derived\n";}

当然还可以这样写也没问题.

 Derived(double cost=0.0, int id = 5):m_cost { cost }{m_id = id; // √std::cout << "Derived\n";}

访问控制

坑踩完了, 上面这些例子都是建立在基类的public数据成员公有继承方式的基础上的, 但实际上数据成员为private 和protected. 关于这里的内容, 就涉及到了访问控制的知识, 完整如下:

一个类成员可以是private, protected, public的:

  • 如果它是private的, 仅被所属类的成员函数和友元函数所使用
  • 如果它是protected的, 仅被所属类的成员函数和友元函数以及派生类的成员函数和友元函数所使用
  • 如果它是public的, 可以被任何函数所使用.

这反映了函数按类访问权限可分为三类:

  • 实现类的函数(其友元和成员)
  • 实现派生类的函数(派生类的函数(派生类的友元和成员)
  • 其它函数

在这里插入图片描述
而派生类和基类的关系就只涉及purblic, 和 protected, 且只有派生类单向访问基类, 所以下来就共有6种

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的provate成员
基类的protected成员派生类的protected成员派生类的protected成员函数派生类provate成员

为什么要取消派生类成员对基类的private成员的访问呢?
答:如果派生类的成语函数刻意访问其基类的私有成员, 这会令私有成员的概念变得毫无意义, 因为程序员简单地从一个类派生出一个新类, 就能获得其私有部分的访问权. 而且我们再也不能通过检查成员函数和友元函数就找到私有成员的使用之处了. 我们必须检查完整程序中涉及派生类的所以源文件, 然后检查这些类的每个函数, 然后再检查这些类的所以派生类, 依次类推. 这样的方式不仅是一项烦人的工作, 而且实际上通常是行不通的. 如果可行, 我们可以使用保护的而非私有的成员.

如果您不选择继承方式,C++ 默认为私有继承(就像如果您不另
行指定,成员默认为私有访问一样)。

接下来我们可以测试验 证一下, 还是上面的测试方法: 我们先选择好继承方式和所访问的基类成员, 然后在子类中给父类的成员赋值, 看是否成功, 如果可以成功就说名可以访问:

还有自身成员函数是可以访问其所在类所以成员的, 而初始化器函数就是自身的成员函数, (没必要protected和public各自定义一个初始化器, 会报重复声明的错误)

结合这两点, 我们可以设计一个基类: 一个protected成员变量, 一个public成员变量. 一个子类:一个protected成员变量, 一个public成员变量; 然后利用初始化器函数子类成员换着给基类的变量赋值就行;

(1)第一组public继承:

类成员/继承方式public继承是否完成
基类的public成员派生类的public成员函数
基类的protected成员派生类的protected成员函数

初始化顺序错, 在类中成员变量谁先声明谁先初始化:
在这里插入图片描述
子类先声明的m_sub_pro, 然后是m_sub_pub ,所以初始化顺序也是这个m_sub_pro, m_sub_pub. 还有赋值的形参顺序.
在这里插入图片描述
正确代码如下:

#include <iostream>
class Base
{
protected:int m_pro {};
public:int m_pub {};Base(int pro=0, int pub=0):m_pro{pro},m_pub{pub}{std::cout << "Base:: " << m_pro << " : " << m_pub << '\n';}void print(){std::cout << m_pro << " : " << m_pub << '\n';}
};class Derived : public Base
{
protected:int m_sub_pro { };
public:int m_sub_pub { };Derived(int subpro=0, int subpub=0):m_sub_pro{subpro},m_sub_pub{subpub}{m_pub = m_sub_pub;m_pro = m_sub_pro;}
};int main()
{Derived d;return 0;
}

在这里插入图片描述
检查基类的初始值, 对;

派生类public成员 ---->基类public成员 对
派生类protected成员 ---->基类protected成员 对

在这里插入图片描述

int main()
{//Derived d;Derived d(1, 2);d.print();return 0;
}

派生类public成员 ---->基类protected成员

在这里插入图片描述

派生类protected成员 ---->基类public成员
在这里插入图片描述
结论:
在这里插入图片描述

(2)第一组protected继承:

类成员/继承方式public继承是否完成
基类的public成员派生类的public成员
基类的protected成员派生类的protected成员

在这里插入图片描述
在这里插入图片描述

为了防止之前的可执行文件没清理而影响, 我又删除了可执行文件

在这里插入图片描述
先执行一遍, 确保没缓存影响
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

结论:可能是由于其他地方或者设计不合理, 测失败.
在这里插入图片描述
我把子类的public成员改成protected了
在这里插入图片描述
最后发现::错误!main()不是派生类,不能调用protected构造函数
这说明需要一个设计一个派生类去调用它, 但所有的函数要经过main函数, 我不会设计了, 暂时放弃了.
结论:缺乏验证的手段, 待定
在这里插入图片描述

(3) private继承

在这里插入图片描述
原因同上, 待定.
再学习着找方法吧

http://www.dtcms.com/a/524992.html

相关文章:

  • 网站 建设运行情况网站移动转换
  • 如何上传ftp网站程序普像工业设计网站
  • 做响应式网站的公司如何做与别人的网站一样的
  • pytorch下对各种超参调整效果
  • 做网站会遇到的问题title 镇江网站建设
  • 怎么做网站底部版权信息在哪可以接企业网站建设的活
  • 聊城网站建设服务好赣州网站制作较好的公司
  • 今日行情明日机会——20251024
  • pip常用命令
  • 杂志网站建设推广方案好的设计作品网站
  • 多语言外贸网站开发wordpress 谷歌地图
  • 简单的电商网站开发网站建设的本质
  • 建行官方网站首页黄页网络的推广
  • 【力扣Hot100】刷题日记
  • IROS 2025现场,触觉力反馈、数据手套遥操作机器人灵巧手平台系统解决方案
  • vivo官方网站进入短视频推广计划
  • 绵阳网站seo网站建设小结
  • 深圳个性化网站建设公司电话做评测好的视频网站
  • N8周打卡:使用Word2vec实现文本分类
  • 开发日记-10-24
  • 如何使用 Python 自动调整 Excel 行高和列宽
  • 私人定制网站装修公司排行榜十大排名
  • 网站建设好后打开是目录文件河北省建筑信息平台
  • 分红盘网站开发多少钱怎么做网站上做电子书
  • 网站后台的网址忘记了房天下搜房网官网
  • 碳中和时代:新能源汽车热管理技术新趋势
  • 爬虫逆向之X音a_bogus参数分析
  • Tigshop开源商城系统 JAVA v5.6.0 版本已发布
  • 自己可以申请网站做外卖吗竞价广告推广
  • 做淘宝联盟网站要多少钱?权威的网站建设