C++语言学习(七)—— 继承、派生与多态(一)

目录

一、派生类的概念

1.1 定义派生类的语法格式

1.1.1 定义单继承派生类

1.1.2 定义多继承派生类

1.2 继承方式

二、公有继承

三、派生类的构造和析构

四、保护成员的引入

五、改造基类的成员函数

六、派生类与基类同名成员的访问方式

七、私有继承和保护继承

7.1 私有继承

7.1.1 类组合与私有继承方式的主要区别

7.2 保护继承

7.3 保护继承与私有继承的异同点


一、派生类的概念

在C++中,派生类是指从另一个类(称为基类或父类)继承所有的成员(包括数据成员和成员函数)的新类。派生类继承了现有类的成员变量和成员函数,并且可以新增自己的成员变量和成员函数。派生类通过继承现有类的属性和行为,可以扩展现有类的功能或者在现有类的基础上进行修改,从而实现代码的复用和扩展。

派生类可以通过继承来获得基类的属性和行为,并且可以根据需要进行修改或扩展。通过继承,派生类可以重用基类的代码,避免了重复编写相似的代码。这样,派生类可以更加灵活和高效地实现特定的功能。

派生类通过使用派生类的名称来创建对象,并且可以使用派生类对象的成员函数来访问和修改继承的成员变量和成员函数派生类也可以重新定义继承的成员函数,称为函数的重写(override),从而修改现有类的行为。此外,派生类还可以定义自己特有的成员变量和成员函数,以实现自己的功能。

1.1 定义派生类的语法格式

1.1.1 定义单继承派生类

在C++中,定义单继承的派生类的语法格式如下:

class DerivedClass : access-specifier BaseClass {// 派生类的成员声明
};

其中,DerivedClass是派生类的名称,access-specifier访问权限修饰符,用于指定派生类对基类成员的访问权限,BaseClass是派生类所继承的基类。

访问权限修饰符有以下三种可能的取值:

  • public: 派生类可以访问基类的公有成员。
  • protected: 派生类可以访问基类的公有成员和保护成员,但不能访问私有成员。
  • private: 派生类不能访问基类的任何成员。

1.1.2 定义多继承派生类

在C++中,定义多继承的派生类的语法格式如下:

class DerivedClass : access-specifier BaseClass1, access-specifier BaseClass2, ..., access-specifier BaseClassN {// 派生类的成员声明
};

其中,DerivedClass是派生类的名称,access-specifier是访问权限修饰符,用于指定派生类对基类成员的访问权限,BaseClass1, BaseClass2, ..., BaseClassN是派生类所继承的多个基类,它们以逗号分隔。

以下是一个示例,定义了一个多继承的派生类DerivedClass,继承自基类BaseClass1BaseClass2

class BaseClass1 {
public:void baseFunction1() {// 基类1成员函数的实现}
};class BaseClass2 {
public:void baseFunction2() {// 基类2成员函数的实现}
};class DerivedClass : public BaseClass1, private BaseClass2 {
public:void derivedFunction() {// 派生类成员函数的实现}
};

在上面的示例中,派生类DerivedClass继承基类BaseClass1BaseClass2,并且可以访问BaseClass1的公有成员,但不能访问BaseClass2的任何成员。派生类还定义了自己的成员函数derivedFunction

1.2 继承方式

在C++中,派生类可以通过不同的继承方式来继承基类的成员。

C++中有三种继承方式,分别是:

  1. 公有继承(public inheritance):使用public关键字指定继承方式。公有继承意味着基类的公有成员在派生类中仍然是公有的,基类的保护成员在派生类中变为保护的,基类的私有成员在派生类中不可访问。

  2. 保护继承(protected inheritance):使用protected关键字指定继承方式。保护继承意味着基类的公有和保护成员在派生类中都变为保护的,基类的私有成员在派生类中不可访问。

  3. 私有继承(private inheritance):使用private关键字指定继承方式。私有继承意味着基类的公有和保护成员在派生类中都变为私有的,基类的私有成员在派生类中不可访问。

继承方式可以通过在派生类定义时使用相应的访问权限修饰符来指定。

以下是一个示例,展示了不同继承方式的使用:

class BaseClass {
public:void publicFunction() {// 公有成员函数实现}protected:void protectedFunction() {// 保护成员函数实现}private:void privateFunction() {// 私有成员函数实现}
};class DerivedClassPublic : public BaseClass {// 此处定义的成员可以访问基类的公有成员和保护成员
};class DerivedClassProtected : protected BaseClass {// 此处定义的成员可以访问基类的公有成员和保护成员
};class DerivedClassPrivate : private BaseClass {// 此处定义的成员可以访问基类的公有成员和保护成员
};

在上面的示例中,DerivedClassPublic使用公有继承,DerivedClassProtected使用保护继承,DerivedClassPrivate使用私有继承。它们分别继承了BaseClass的公有成员和保护成员,并且在派生类中具有相应的访问权限。

二、公有继承

公有继承(public inheritance)是面向对象编程中的一种继承方式,它表示派生类可以继承基类的公有成员。在公有继承中,派生类可以访问基类的公有成员,包括公有的方法和属性,而对于基类的私有成员,派生类无法直接访问

使用公有继承时,派生类会继承基类的成员函数和成员变量,并且可以通过对象进行访问。派生类可以直接使用基类的公有成员,无需进行任何额外的操作。对于基类的公有成员,派生类可以直接使用,并且可以对其进行重载。此外,派生类也可以添加自己的成员函数和成员变量。

公有继承的特点包括:

  • 派生类可以访问基类的公有成员(方法和属性)
  • 派生类可以对基类的公有成员进行重载
  • 派生类可以添加自己的成员函数和成员变量

公有继承是面向对象编程中最常用的继承方式之一,它能够提供良好的代码复用性和扩展性,同时也符合了封装的原则。

注意:

  • 过度使用继承可能会导致类之间的耦合性增强,增加了代码的复杂性,因此在设计时需要谨慎使用公有继承。

三、派生类的构造和析构

派生类的构造函数和析构函数分别在创建和销毁派生类的对象时被调用。

构造函数用于初始化派生类对象的成员变量和调用基类的构造函数。在派生类的构造函数中,首先调用基类的构造函数来初始化继承的成员变量,然后再对派生类自身的成员变量进行初始化。如果派生类没有定义自己的构造函数,编译器会自动生成一个默认的构造函数。

析构函数用于销毁派生类对象时的清理工作。在派生类的析构函数中,先调用派生类自身的析构函数,然后再调用基类的析构函数。如果派生类没有定义自己的析构函数,编译器会自动生成一个默认的析构函数。

派生类的构造函数和析构函数的调用顺序是先基类的构造函数,然后派生类的构造函数,销毁对象时的顺序相反,先派生类的析构函数,然后基类的析构函数。

在派生类的构造函数和析构函数中,可以通过使用 关键字 " : : " 来调用基类的构造函数和析构函数。例如:

#include <iostream>
using namespace std;
class Base {
public:Base() {cout << "Base constructor called" << endl;}~Base() {cout << "Base destructor called" << endl;}
};class Derived : public Base {
public:Derived() {cout << "Derived constructor called" << endl;}~Derived() {cout << "Derived destructor called" << endl;}
};int main() {Derived d;return 0;
}

输出结果为:

在这个例子中,派生类Derived继承了基类Base的构造函数和析构函数。创建Derived对象时,先调用基类Base的构造函数,然后再调用派生类Derived的构造函数。销毁Derived对象时,先调用派生类Derived的析构函数,然后再调用基类Base的析构函数。

四、保护成员的引入

当我们在派生类中需要直接访问基类的成员时,可以通过将这些成员声明为protected来引入保护成员。这样,派生类可以直接访问基类的成员,而不需要通过基类的公有接口来访问。

下面是一个示例,说明如何在派生类中引入保护成员:

class Base {
protected:int protectedMember; // 声明一个保护成员public:Base(int value) : protectedMember(value) { } // 基类的构造函数初始化保护成员void display() {cout << "Protected member: " << protectedMember << endl;}
};class Derived : public Base {
public:Derived(int value) : Base(value) { } // 派生类的构造函数调用基类的构造函数void updateProtectedMember(int value) {protectedMember = value; // 在派生类中直接访问基类的保护成员}
};

在上面的示例中,我们有一个基类Base和派生类Derived。在基类Base中,我们声明了一个protected成员protectedMember。在构造函数中,我们使用初始化列表来初始化保护成员。

在派生类Derived中,我们使用public继承了基类Base。由于protectedMember在基类中是保护的,因此在派生类中可以直接访问它。在派生类的成员函数updateProtectedMember中,我们直接对protectedMember进行赋值操作。在派生类的构造函数中,我们使用初始化列表来调用基类的构造函数以初始化保护成员。

通过引入保护成员,派生类Derived可以直接访问和修改基类Base的保护成员protectedMember,而不需要通过基类的公有接口来访问。这样可以简化代码,并提高代码的可读性和效率。

五、改造基类的成员函数

要改造基类的成员函数,可以通过在派生类中重新定义该成员函数来实现。

  1. 如果想在派生类中完全重写基类的成员函数,可以使用相同的函数名和参数列表,在派生类中重新实现该函数。这将会隐藏基类的成员函数,在派生类对象上调用该函数时将只执行派生类中的实现,不会执行基类中的实现。

  2. 如果想在派生类中对基类的成员函数进行扩展,可以使用相同的函数名和参数列表,在派生类中重新实现该函数,并在派生类中调用基类的成员函数。这样可以先执行基类的实现,再执行派生类的实现。

下面是一个示例代码:

class Base {
public:void foo() {// 基类的实现// ...}
};class Derived : public Base {
public:void foo() {// 完全重写基类的成员函数// ...}
};class Derived2 : public Base {
public:void foo() {// 对基类的成员函数进行扩展Base::foo(); // 调用基类的成员函数// 扩展派生类的实现// ...}
};

六、派生类与基类同名成员的访问方式

派生类中存在与基类同名的成员时,可以使用以下几种方式来访问这些成员

  • 使用作用域解析运算符(: :):可以使用基类名加上作用域解析运算符来访问基类中的同名成员。
class Base {
public:int data;
};class Derived : public Base {
public:void accessBaseMember() {Base::data = 10; // 使用作用域解析运算符来访问基类的成员}
};

  • 使用this指针:this指针指向当前对象,可以用来访问当前对象中的成员变量和成员函数。在派生类中,可以使用this指针来访问基类的同名成员。
class Base {
public:int data;
};class Derived : public Base {
public:void accessBaseMember() {this->data = 10; // 使用this指针来访问基类的成员}
};

  • 使用基类名进行访问:使用派生类对象的基类部分来访问基类的同名成员。
class Base {
public:int data;
};class Derived : public Base {
public:void accessBaseMember() {Base::data = 10; // 使用基类名进行访问基类的成员}
};

  • 直接访问:如果派生类中没有与基类同名的成员变量,可以直接访问基类的同名成员。
class Base {
public:int data;
};class Derived : public Base {
public:void accessBaseMember() {data = 10; // 直接访问基类的成员}
};

七、私有继承和保护继承

7.1 私有继承

私有继承是一种继承方式,它使得基类的公有成员和保护成员在派生类中变为私有成员,而私有成员在派生类中不可访问。

私有继承的语法格式为:

class Derived : private Base {};        //Derived是派生类,Base是基类。

私有继承的特点包括:

  1. 基类的公有成员和保护成员在派生类中变为私有成员,只能在派生类内部访问。
  2. 派生类对象不能直接访问基类的成员函数和成员变量,需要通过派生类的成员函数间接访问。
  3. 派生类可以重写基类的虚函数,但由于私有继承的特性,基类的虚函数在派生类中也变为私有成员,无法被外部访问。
  4. 私有继承主要用于实现组合关系,将基类的实现细节隐藏在派生类中,提供了一种方式来重用基类的实现。

私有继承在设计和实现中的常见应用包括:

  1. 实现封装:通过私有继承,派生类可以从基类继承私有成员,从而实现对基类实现细节的封装。
  2. 实现子类化:通过私有继承,派生类可以重用基类的实现,并添加自己的功能和行为。
  3. 实现特化:通过私有继承,可以对基类进行特化,使派生类专门适用于某些特定的场景或需求。

7.1.1 类组合与私有继承方式的主要区别

类组合和私有继承是实现代码复用的两种不同方式。

  1. 类组合:

    • 类组合是一种对象关系,其中一个类(成员对象)是另一个类的成员。
    • 在类组合中,一个类(组合类)包含另一个类(成员类)的对象作为它的成员变量。
    • 组合类可以通过调用成员类的公有成员函数来访问成员类的功能和行为。
    • 组合关系通常是“一个拥有另一个”的关系,可以灵活地在运行时更换成员对象。
  2. 私有继承:

    • 私有继承是一种继承关系,派生类继承基类的实现和接口。
    • 在私有继承中,派生类通过私有继承基类,将基类的公有成员和保护成员变为私有成员。
    • 私有继承将基类的实现细节隐藏在派生类中,派生类通过自身的公有接口来访问基类的功能。
    • 私有继承通常是“是一个”的关系,派生类是基类的一种特化或子类。

主要区别:

  1. 访问权限:在私有继承中,派生类只能在自己的成员函数中访问基类的成员,而在类组合中,组合类可以直接访问成员类的公有成员。
  2. 关系类型:类组合是一种对象关系,表示一个类拥有另一个类的对象作为成员;私有继承是一种继承关系,表示派生类是基类的一种特化或子类。
  3. 灵活性:类组合具有更大的灵活性,可以在运行时更换成员对象;私有继承在编译时确定基类类型,不可更换。

7.2 保护继承

保护继承是一种继承方式,它指的是在继承关系中,子类可以继承父类的成员(属性和方法),但不能访问父类的私有成员。

保护继承的目的是为了保护父类的实现细节,避免子类对父类的私有属性和方法进行直接访问和修改,从而提高代码的安全性和可维护性。

为了实现保护继承,可以在父类中使用访问修饰符来限制成员的访问权限。在C++中,可以使用protected关键字来修饰父类的成员,表示这些成员可以被子类访问,但不能被外部类或对象访问。

以下是一个使用保护继承的简单示例:

class Parent {
protected:int protectedMember;public:void publicMethod() {// 父类的公有方法}protected:void protectedMethod() {// 父类的保护方法}
};class Child : public Parent {
public:void childMethod() {protectedMember = 10;  // 子类可以访问父类的保护成员protectedMethod();  // 子类可以调用父类的保护方法}
};int main() {Child child;child.publicMethod();  // 子类可以调用父类的公有方法child.childMethod();   // 子类自己的方法// child.protectedMember;  // 无法访问父类的保护成员,会编译错误// child.protectedMethod();  // 无法调用父类的保护方法,会编译错误return 0;
}

在这个示例中,Parent类有一个保护成员protectedMember和一个保护方法protectedMethod,它们都可以被子类Child访问和调用。但在main函数中,无法直接访问protectedMember和protectedMethod,只能通过调用子类的方法间接访问。

7.3 保护继承与私有继承的异同点

保护继承和私有继承是两种不同的继承方式,在继承关系中有一些异同点。

异同点如下:

  1. 访问权限:

    • 保护继承:子类可以访问父类的受保护成员和公有成员,但不能访问私有成员。
    • 私有继承:子类不能直接访问父类的任何成员,包括公有、受保护和私有成员。
  2. 外部访问:

    • 保护继承:子类的对象不能访问父类的受保护和私有成员,只能通过子类的接口进行访问。
    • 私有继承:子类的对象不能访问父类的任何成员,包括公有、受保护和私有成员。
  3. 类型转换:

    • 保护继承:子类对象可以隐式转换为父类对象,但不能隐式转换为父类子类的对象。
    • 私有继承:子类对象不能进行隐式类型转换。
  4. 封装性:

    • 保护继承:保护继承提供了一种方式来封装父类的实现细节,并且可以在子类中使用父类的接口进行操作,提高了代码的封装性和安全性。
    • 私有继承:私有继承将父类的所有成员都隐藏在子类的内部,外部无法访问父类的任何成员,实现了更强的封装性。​​​​​​​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340869.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zdppy_api 中间件请求原理详解

单个中间件的逻辑 整体执行流程&#xff1a; 1、客户端发起请求2、中间件拦截请求&#xff0c;在请求开始之前执行业务逻辑3、API服务接收到中间件处理之后的请求&#xff0c;和数据库交互&#xff0c;请求数据4、数据库返回数据5、API处理数据库的数据&#xff0c;然后给客户…

探索数据结构:快速排序与归并排序的实现与优化

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 快速排序 1.1. 算法思想 **快速排序(Quick Sort)**是Hoare于1962年…

【数据结构】穿梭在二叉树的时间隧道:顺序存储的实现

专栏引入 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累。我想让大家…

AI程序员来了,大批码农要失业

根据GitHub发布的《Octoverse 2021年度报告》&#xff0c;2021年中国有755万程序员&#xff0c;排名全球第二。 ChatGPT的出现&#xff0c;堪比在全球互联网行业点燃了一枚“核弹”&#xff0c;很多人都会担心“自己的工作会不会被AI取代”。 而2024年的AI进展速度如火箭般&am…

getway整合sentinel流控降级

3. 启动sentinel控制台增加流控规则&#xff1a; 根据API分组进行流控&#xff1a; 1.设置API分组&#xff1a; 2.根据API分组进行流控&#xff1a; 自定义统一异常处理&#xff1a; nginx负载配置&#xff1a;

vue-router 源码分析——2. router-link 组件是如何实现导航的

这是对vue-router 3 版本的源码分析。 本次分析会按以下方法进行&#xff1a; 按官网的使用文档顺序&#xff0c;围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码&#xff0c;更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升&#xff0c;甚至面试时…

德人合科技——@天锐绿盾 | -文档透明加密系统

天锐绿盾文档透明加密系统是一种先进的数据安全解决方案&#xff0c;旨在保护企业和组织的敏感信息&#xff0c;防止未经授权的访问和泄漏。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是该系统的一些关键特点和功…

【读书笔记】曼陀罗思考法

目录 1 起源2 路径示例——人生规划设计 3 分类3.1 扩展型“扩展型”曼陀罗——使用方法 3.2 围绕型 4 注意事项 1 起源 曼陀罗在梵文中意味着“圣地”&#xff0c;象征着宇宙的秩序和内心的神圣结构。 “曼陀罗思考法”&#xff0c;是由日本学者今泉浩晃发明的方法&#xff…

【计算机毕设】基于SpringBoot的中小企业设备管理系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 在中小企业中&#xff0c;设备管理是确保生产和运营效率的重要环节。传统的设备管理通常依赖于手工记录和人工管理&#xff0c;容易导致数据不准确、…

LLM的基础模型4:初识Embeddings

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

12-学生们参加各科测试的次数(高频 SQL 50 题基础版)

12-学生们参加各科测试的次数 -- 学生表中&#xff0c;id是唯一的&#xff0c;将他作为主表 -- CROSS JOIN产生了一个结果集&#xff0c;该结果集是两个关联表的行的乘积 -- 2行表,与3行表使用cross join,得到2*36行数据 select st.student_id, st.student_name,su.subject_na…

【软件测试】自动化测试如何管理测试数据

前言 在之前的自动化测试框架相关文章中&#xff0c;无论是接口自动化还是UI自动化&#xff0c;都谈及data模块和config模块&#xff0c;也就是测试数据和配置文件。 随着自动化用例的不断增加&#xff0c;需要维护的测试数据也会越来越多&#xff0c;维护成本越来越高&#…

【Transformer(7)】Transformer架构解析

一、Transformer结构图 从上图可以看到&#xff1a; Transformer结构主要由编码和解码两大部分组成&#xff1a; &#xff08;1&#xff09;输入- position embedding - patch embedding &#xff08;2&#xff09;编码器 多头注意力机制 Add & NormMLP Add & Norm &…

爪哇,我初窥门径

2017年3月&#xff0c;我大二下学期了。 虽说一直在学习&#xff0c;持续在解决学习中遇到的问题&#xff0c;但迷茫依旧。 对着黑框编程&#xff0c;还是不知道Java在现实工作中是用来干什么的。 说实话&#xff0c;真的挺枯燥无趣的。 逐渐&#xff0c;我开始意识到&#…

linux部署运维2——centos7.9离线安装部署涛思taos2.6时序数据库TDengine

在实际项目开发过程中&#xff0c;并非一直都使用关系型数据库&#xff0c;对于工业互联网类型的项目来说&#xff0c;时序型数据库也是很重要的一种&#xff0c;因此掌握时序数据库的安装配置也是必要的技能&#xff0c;不过对于有关系型数据库使用的开发工作者来说&#xff0…

基础数学内容重构(后缀0个数)

今天也是参加了一下宁波大学的校赛&#xff0c;其中有一道题是求后缀0的个数&#xff0c;题意是让我们求一下式子的后缀0个数&#xff1a; 看上去比较复杂&#xff0c;但是通过化简我们可以知道以上式子就是求&#xff08;n 1&#xff09;&#xff01;&#xff0c;这里化简的过…

windows上进行git初始化时报错:fatal: unknown write failure on standard output

一、报错描述 1、git init命令一般是在命令行&#xff0c;切换到项目的根目录后执行 2、如果是windows的系统&#xff0c;我们粘贴路径时&#xff0c;需要进行转义命令行才能识别&#xff0c; 也就是像我下面写的 D:\\Users\\...3、报错信息进行解读 一般情况下&#xff0c;…

2024年手机能做的赚钱软件有哪些?整理了八个手机能做的正规赚钱软件分享

在这个指尖滑动的时代&#xff0c;手机不仅仅是通讯工具&#xff0c;更是我们探索财富的钥匙。你是否曾幻想过&#xff0c;躺在沙发上&#xff0c;轻轻一滑&#xff0c;就能让钱包鼓起来&#xff1f; 今天&#xff0c;就让我们一起来探索那些隐藏在手机里的赚钱秘笈&#xff0c…

Apache OFBiz 路径遍历导致RCE漏洞复现(CVE-2024-36104)

0x01 产品简介 Apache OFBiz是一个电子商务平台,用于构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类应用系统。是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。 0x02 漏洞概…

缓存方法返回值

1. 业务需求 前端用户查询数据时,数据查询缓慢耗费时间; 基于缓存中间件实现缓存方法返回值:实现流程用户第一次查询时在数据库查询,并将查询的返回值存储在缓存中间件中,在缓存有效期内前端用户再次查询时,从缓存中间件缓存获取 2. 基于Redis实现 参考1 2.1 简单实现 引入…