【C++】继承

embedded/2024/9/23 10:23:52/

目录

一、继承的概念

1.继承的引入

2.继承的格式

二、继承的基本语法

1.不同继承方式的区别

2.赋值兼容规则

3.派生类中的相关函数

4.函数隐藏

三、虚继承


一、继承的概念

1.继承的引入

在实际生活中我们会遇到很多有着共同特性的事物,例如,我们定义一个学生类和教师类,可以发现成员变量中都将会有姓名、性别、年龄等相关信息,这样的例子还有很多,如果每次写时都要包含这些共同信息就会很麻烦,因此,继承就应运而生。

继承机制是面向对象程序设计中使代码可以复用的重要手段,它允许程序员在保证原有类特性的基础上进行扩展,增加功能,这样产生的新的类称作派生类。

例如在上面的例子中,我们可以写一个人类(成员中有姓名、性别等),然后学生类和教师类可以继承人类,再根据其特性增加相应成员变量和成员函数,在这个例子中人类称作基类(父类),学生类和教师类称作派生类(子类)

2.继承的格式

class 基类 {
    // 基类的成员
};

class 派生类 : 继承方式 :基类 {
    // 派生类的成员
};

其中继承方式有public、protected和private。

继承有单继承和多继承,当继承了多个类时,依旧是在派生类后写继承方式+继承的类名,注意用逗号分隔,当我们不写继承方式时默认继承方式是private

二、继承的基本语法

1.不同继承方式的区别

继承方式public继承protected继承private继承
基类的public成员为派生类中的public成员为派生类中的protected成员为派生类中的private成员
基类的protected成员为派生类中的protected成员为派生类中的protected成员为派生类中的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

从上面的表格中可以看到, 无论是哪种继承方式,基类中的private成员在派生类中都是不可见的,不可见即不能直接在派生类中访问,而其他的成员的访问权限则取继承方式和原本基类中的访问权限较小的那一个,即min(继承方式,在基类中的访问权限),这也与权限可以缩小而不能放大相对应。

接下来我们来看段代码来加深理解:

 可以看到,是不能在Derived类中直接访问Base类中的私有成员变量cc的,但是可以访问公有和保护成员变量,如果是private继承也同样如此,因为在类中是可以直接访问成员变量的,不受访问限定符的限制,但是由于基类中私有成员始终不可见,所以仍不能访问基类中的私有成员。

同时,我们也可以发现,protected的作用出现了,当我们不想让某个成员在类外访问,但想让它在派生类中可访问时,就可以在基类中设置为protected。

2.赋值兼容规则

1)本类的对象指针和对象引用可以指向本类对象

2)基类指针和基类引用可以指向派生类对象,基类对象也可以被赋值为派生类对象

3)派生类的指针和引用可以赋值给基类的指针和引用

这里主要对第二、三点进行讲解:

 派生类对象赋值给基类对象的过程通常被称为“切割”,这一过程涉及将派生类对象中的基类部分的数据“切”出来,然后赋值或初始化相应的基类对象、指针或引用。因为我们知道继承后,派生类中是会存在基类中的成员的,当把派生类对象赋值给基类对象时就相当于把这部分切割出来重新赋值。

需要注意的是,派生类的对象可以赋值给基类对象,但是反过来不可以,这个通过上边的“切割”就比较容易理解了。

下面,举具体的例子来演示:

 可以看到,退出代码为0,是没有问题的。

3.派生类中的相关函数

派生类不能继承基类中的构造函数、析构函数、赋值运算符重载函数以及友元函数。

派生类中基类成员的构造必须由基类构造函数完成,不能由派生类的构造函数完成,并且是先初始化基类成员,再初始化其他成员,而析构函数则与之相反。

对于基类的构造函数有两种方式,即显式调用和隐式调用,显式调用即在初始化列表中显式调用基类构造函数,隐式调用则是当基类中有默认构造函数时,我们不显式调用基类的构造函数,编译器会自动先调用基类的默认构造函数,然后执行派生类构造函数函数体。

需要注意的是,显式调用基类构造函数初始化派生类对象中的基类数据成员必须使用成员初始化列表

析构函数不需要我们显式调用,在执行完派生类的析构函数后会自动调用基类的析构函数。

派生类的拷贝构造函数和赋值运算符重载函数都必须调用基类的拷贝构造函数和赋值运算符重载函数完成基类的拷贝初始化和赋值。

接下来简单看个示例:

class Base
{
public:Base():aa(0){}Base(Base& x):aa(x.aa){}
private:int aa;
};class Derived:public Base
{
public:Derived():Base(),bb(1){}Derived(Derived& x):Base(x),bb(x.bb)  //赋值兼容转换{}private:int bb;
};

因为有了赋值兼容转换规则,派生类对象可以直接赋值给基类对象,所以上面派生类的拷贝构造函数中可以直接传派生类对象给基类拷贝构造函数,如果要在函数体内调用基类拷贝构造函数的话,得加类名,即Base::Base(x);。

友元关系不能继承,基类中的友元函数或者友元类是并不能被派生类继承的,即不能访问派生类的私有成员。

4.函数隐藏

基类和派生类都是独立的作用域,他们是可以有同名成员的,当有同名成员并且使用时,默认调用的就是派生类中的成员,如果要调用基类中的同名成员需要加上类名来显式调用,这种对基类同名成员的屏蔽称作隐藏(亦重定义)

三、虚继承

 在生活中也存在一些特殊的继承关系,例如,某个某个程序员他既是一名员工,也是一名父亲,而员工和父亲类都可以由人类继承过来,这样当继承员工类和父亲类时就会有两份人类中的成员数据,当在调用时就可能发生歧义并且会造成冗余,为了解决这个问题,虚继承就产生了。

虚继承用到的关键字是virtual。

格式如下:

class 基类 {
    // 基类的成员
};

class 中间类1 : virtual public 基类 {
    // 中间类1的成员
};

class 中间类2 : virtual public 基类 {
    // 中间类2的成员
};

class 最派生类 : public 中间类1, public 中间类2 {
    // 最派生类的成员
};

 在虚继承中,基类成员数据就只有一份,避免了数据冗余和空间浪费。

在虚继承中,对最派生类的构造不仅需要调用直接基类的构造函数,还需要调用最基类的构造函数,因为这样可以保证最基类可以只构造一次。

举个例子:

class Base {
public:Base() { std::cout << "Base constructor called.\n"; }
};class Derived1 : virtual public Base {
public:Derived1() { std::cout << "Derived1 constructor called.\n"; }
};class Derived2 : virtual public Base {
public:Derived2() { std::cout << "Derived2 constructor called.\n"; }
};class MostDerived : public Derived1, public Derived2 {
public:MostDerived() : Base(), Derived1(), Derived2() {std::cout << "MostDerived constructor called.\n";}
};


http://www.ppmy.cn/embedded/58954.html

相关文章

需求实现:字数限制500字

摘要&#xff1a; 在浩瀚的技术实现海洋中&#xff0c;每一个需求都如同一块磁石&#xff0c;吸引着多样化的解决策略与技术手段的汇聚。然而&#xff0c;无论选择多么丰富&#xff0c;核心始终在于对业务逻辑的深刻理解&#xff0c;以及为当前特定场景量身定制的最佳实践。本…

uni-app 保存号码到通讯录

1、 添加模块 2、添加权限 3、添加策略 Android&#xff1a; "permissionExternalStorage" : {"request" : "none","prompt" : "应用保存运行状态等信息&#xff0c;需要获取读写手机存储&#xff08;系统提示为访问设备上的照片…

Javassist 修改 jar 包里的 class 文件

前言 Javassist 是一个用于处理 Java 字节码的类库&#xff0c;可以用以修改 class 文件或 jar 包里的 class 文件。 简单来说我们用Java编写的代码是放在 java 格式的代码文件里&#xff0c;在编译的时候会编译为 class 格式的字节码文件&#xff0c;然后一般所有 class 文件…

探索4D毫米波雷达和摄像头在自动驾驶中的潜力

随着自动驾驶技术的快速发展&#xff0c;关于各种传感器的必要性&#xff0c;尤其是LiDAR&#xff08;激光雷达&#xff09;与毫米波雷达结合摄像头的作用&#xff0c;激发了激烈的讨论。在这篇博客中&#xff0c;我们将探讨4D毫米波雷达和摄像头的组合是否可能成为自动驾驶车辆…

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测 目录 SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现NGO-CNN-LSTM-Mutilhead-Attention北方苍鹰算…

【Docker 系列】学习路线

学习基本概念&#xff1a; 了解容器化与虚拟化的区别了解Docker的基本概念、术语和架构 安装Docker&#xff1a; 根据所使用的操作系统&#xff0c;安装Docker Desktop&#xff08;Windows、macOS&#xff09;或Docker Engine&#xff08;Linux&#xff09; Docker镜像&#xf…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥删除(ArkTS)】

密钥删除(ArkTS) 为保证数据安全性&#xff0c;当不需要使用该密钥时&#xff0c;应该删除密钥。 开发步骤 以删除HKDF256密钥为例。 确定密钥别名keyAlias&#xff0c;密钥别名最大长度为64字节。初始化密钥属性集。用于删除时指定密钥的属性TAG&#xff0c;比如删除的密钥…

富格林:正确击破暗箱稳健出金

富格林悉知&#xff0c;现在喜欢投资的人都在不断增加&#xff0c;在享受贵金属投资较高出金回报的同时&#xff0c;也该做好遭遇暗箱亏损情况的预防措施。对于新手投资者群体来说&#xff0c;如何客观地认识造成亏损的原因和暗箱&#xff0c;也是贵金属投资道路上必须克服的一…