C++ 继承和派生

继承和派生

一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。
从父类产生一个子类,称为派生。
基类与派生类的关系:派生类是基类的具体化,而基类是派生类的抽象。

定义基类和派生类

定义基类

例:一个矩形类。

/*矩形类*/
class Rectangle
{
protected:/*被保护成员*/double L = 0;double W = 0;
public:   /*共有成员*/Rectangle() = default;Rectangle(double a, double b) : L{ a }, W{ b } {}double GetArea() const	/*矩形面积*/{return L * W;}double GetGirth() const	/*矩形周长*/{return  2 * (L + W);}
};
基类成员的访问属性
  • 共有成员(public member)可以在程序的任何地方被访问。
  • 私有成员(private member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用。
  • 被保护成员(protected member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用,但可以被派生类的成员函数使用。

如果省略成员访问限定符,则系统默成员都是私有的。
public、private、protected 关键字被称为成员访问限定符。

定义派生类

派生类必须通过使用类派生列表(class derivation list)明确指出它是从哪个(或哪些)基类继承而来的。
类派生列表的形式是:

class  派生类名:[继承方式]  基类名
{派生类新增加的成员
};

首先是一个冒号,后面紧跟以逗号分隔的基类列表。
其中每个基类前面可以有以下三种访问说明符中的一个:public、protected 或者 private。

派生类的构造函数

派生类可以接收基类全部的成员,但是不能继承构造函数和析构函数。
派生类必须使用基类的构造函数来初始化它的基类部分。
例:

/*长方体类*/
class Cuboid : public Rectangle
{
private:double H = 0;
public:Cuboid() = default;Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}// Rectangle{ a,b }是基类的构造函数
};

执行派生类构造函数的顺序:
先调用基类构造函数,对基类数据成员初始化;
然后执行派生类构造函数本身,对派生类数据成员初始化。

派生类的拷贝构造和移动构造函数

在默认情况下,基类默认构造函数初始化派生类对象的基类部分。
如果我们想拷贝((或移动)基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝(或移动)构造函数。

合成的拷贝、赋值、移动操作

某些定义基类的方式也可能导致有的派生类成员成为被删除的函数:

  • 如果基类中的默认构造函数、拷贝构造函数、拷贝赋值运算符或析构函数是被删除的函数或者不可访问,则派生类中对应的成员将是被删除的。
  • 如果在基类中有一个不可访问或删除掉的析构函数,则派生类中合成的默认和拷贝构造函数将是被删除的。
  • 如果基类的移动操作是删除的或不可访问的,则派生类的移动操作也将是被删除的。
  • 如果基类的析构函数是删除的或不可访问的,则派生类的移动构造函数也将是被删除的。
继承的构造函数

C++11标准允许派生类继承基类构造函数的方式是提供一条注明了(直接)基类名的 using 声明语句。

/*正方形类*/
class Square
{
protected:double L;
public:Square() :L{ 4 } {};Square(double a) :L{ a } {}
};
/*立方体类*/
class Cube : public Square
{
public:Square::Square; 	
/*
等价于下列构造函数:
Cube() :Square() {};
Cube(double a) :Square{ a } {}
*/
};

一个构造函数的 using 声明不会改变该构造函数的访问级别。
一个 using 声明语句不能指定 explicit 或 constexpr。

继承方式

通过指定继承方式,可以改变基类成员在派生类中的访问属性。
公用继承
公用继承(public inheritance)基类的共有成员和被保护成员在派生类中保持原有访问属性。
派生类可以访问基类的共有成员和被保护的成员。

class Cuboid : public Rectangle
{
private:double H = 0;
public:Cuboid() = default;Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}double GetVolume() const           /*长方体体积*/{//访问基类被保护成员L、Wdouble V = L * W * H;  //底面积乘高return V;}double GetSurfaceArea() const   /*长方体表面积*/{//访问基类共有成员GetArea(),GetGirth()double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积return S;}
};

派生类的对象可以访问基类的共有成员和本类的共有成员。

	Cuboid Cu{ 3,3,3 };cout << "底面积:" << Cu.GetArea() << endl;cout << "体积:" << Cu.GetVolume() << endl;cout << "底周长:" << Cu.GetGirth() << endl;cout << "表面积:" << Cu.GetSurfaceArea() << endl;

私有继承和受保护的继承
受保护的继承(protected inheritance)基类的共有成员和被保护成员在派生类中成了被保护成员。
私有继承(private inheritance)基类的共有成员和被保护成员在派生类中成了私有成员。
派生类可以访问基类的共有成员和被保护的成员。

class Cuboid : protected Rectangle
{
private:double H = 0;
public:Cuboid() = default;Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}double GetVolume() const   /*长方体体积*/{//访问基类被保护成员L、Wdouble V = L * W * H;  //底面积乘高return V;}double GetSurfaceArea() const   /*长方体表面积*/{//访问基类共有成员GetArea(),GetGirth()double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积return S;}
};

如果基类的共有成员在派生类中成了被保护或私有成员,则派生类的对象只可以访问本类的共有成员。

	Cuboid Cu{ 3,3,3 };cout << "体积:" << Cu.GetVolume() << endl;cout << "表面积:" << Cu.GetSurfaceArea() << endl;
改变个别成员的可访问性

有时我们需要改变派生类继承的某个名字的访问级别,通过使用 using 声明可以达到这一目的:

/*长方体类*/
class Cuboid : public Rectangle
{
private:double H = 0;
public:using Rectangle::W; //受保护的成员变成共有成员Cuboid() = default;Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
};

派生类到基类的隐式类型转换

  1. 派生类对象能向基类对象赋值。(派生类增加的部分会被切掉。)
  2. 派生类对象能向基类对象的引用进行赋值或初始化。
  3. 派生类对象的地址能赋给指向基类对象的指针变量。
  4. 如果函数参数是基类的对象或基类对象的引用,相应的实参能用派生类对象。

通过基类的对象、基类对象的引用或指向基类的指针,只能访问派生类中基类部分的共有成员,而不能访问派生类增加的成员。
不存在基类向派生类的隐式类型转换。

防止继承的发生

有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑它是否适合作为一个基类。C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字 final。

/*矩形类*/
class Rectangle final  //不允许被继承
{
protected:/*被保护成员*/double L = 0;double W = 0;
public:   /*共有成员*/Rectangle() = default;Rectangle(double a, double b) : L{ a }, W{ b } {}
};

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

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

相关文章

rabbitMQ的扇出模式(fanout发布订阅)的生产者与消费者使用案例

扇出模式 fanout 发布订阅模式 生产者 生产者发送消息到交换机&#xff08;logs&#xff09;,控制台输入消息作为生产者的消息发送 package com.esint.rabbitmq.work03;import com.esint.rabbitmq.RabbitMQUtils; import com.rabbitmq.client.Channel;import java.util.Scanne…

使用FP8加速PyTorch训练

现代的人工智能硬件架构(例如&#xff0c;Nvidia Hopper, Nvidia Ada Lovelace和Habana Gaudi2)中&#xff0c;FP8张量内核能够显著提高每秒浮点运算(FLOPS)&#xff0c;以及为人工智能训练和推理工作负载提供内存优化和节能的机会。 在这篇文章中&#xff0c;我们将介绍如何修…

Arduino驱动LM35线性温度传感器(温湿度传感器)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 LM35半导体的温度传感器,可以用来对环境温度进行定性的检测。LM35半导体温度传感器是美国国家半导体公司生产的线性温度传感器。其测温范围是-40℃到150℃,灵敏度为10mV/℃,输出电压与温度成正比。

<C++> 反向迭代器

我们知道正向迭代器的设计&#xff1a;begin迭代器指向第一个数据&#xff0c;end迭代器指向最后一个数据的下一个位置 。移向下一个数据&#xff0c;解引用得到数据的值&#xff0c;并根据容器储存方式的不同&#xff0c;容器有不同类型的迭代器。 注意&#xff1a;rbegin迭代…

c语言:模拟实现qsort函数

qsort函数的功能&#xff1a; qsort相较于冒泡排序法&#xff0c;不仅效率更快&#xff0c;而且能够比较不同类型的元素&#xff0c;如&#xff1a;浮点数&#xff0c;结构体等等。这里我们来模拟下qsort是如何实现这一功能的&#xff0c;方便我们对指针数组有一个更深层次的理…

龙芯 操作系统选择和安装

龙芯3a5000及之后的cpu底层架构已经从mips64el改为了loongarch64 所以这里分了2种来说明&#xff0c;分别对应3a4000之前的和3a5000之后的 龙芯的系统安装难点在于操作系统的选取和引导 一、烧录工具 制作安装盘使用常规的烧录工具是不行滴&#xff0c;会提示没有\boot\initrd…

webpack的安全保障是怎么做的?

文章目录 前言Webpack 内容安全策略后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;webpack &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#xff0c;感…

阿里云linux升级新版本npm、nodejs

在阿里云服务器上编译部署NextJS工程发现 alibaba linux默认yum install npm安装的版本太低, 使用以下方式升级node、npm新版本。 1、卸载现有版本 yum remove nodejs npm -y2、安装新版本 sudo yum install https://rpm.nodesource.com/pub_21.x/nodistro/repo/nodesource-…

STM32的启动流程

1、STM32上电启动的主要步骤 a、初始化堆栈指针sp_initial_sp&#xff0c;初始化PC指针pcReset_Handler。 b、初始化中断向量表。 c、配置系统时钟。 d、调用 C 库函数_main 初始化用户堆栈&#xff0c;然后进入 main 函数。 2、STM32的三种启动模式 复位后&#xff0c;在 S…

从底层原理看Android的序列化是如何实现的

对于Java的序列化&#xff0c;我们可以认为是在数据传输的时候的一套协议或者是一个标准&#xff0c;因为Java存在自己特定的一个数据结构&#xff08;class&#xff09;&#xff0c;举个例子 data class User(val name: String,val age: Int )User是一个对象&#xff0c;我们…

产品经理必备技能:如何快速锁定种子用户群体?

大家好&#xff0c;我是小米&#xff0c;一名热爱技术、热衷分享的90后小青年。今天我们要探讨的话题是一个在产品经理面试中经常被问到的问题&#xff1a;“产品上线后的种子用户该如何获取&#xff1f;”作为一个热爱挑战、乐于探讨的小伙伴&#xff0c;我将和大家分享一些我…

一、MySQL.pratice.search

MySQL是一种常用的关系型数据库管理系统&#xff0c;广泛应用于各种Web应用程序中。在编程中&#xff0c;使用MySQL进行数据操作是非常常见的操作。在MySQL中&#xff0c;查询是最常用的操作之一&#xff0c;可以查询整个表或者根据特定的条件查询数据。 文章目录 一、查询&am…

第七部分:Maven(项目管理工具)

目录 Maven简介 7.1&#xff1a;为什么学习Maven&#xff1f; 7.1.1、Maven是一个依赖管理工具 7.1.2&#xff1a;Maven是一个构建工具 7.1.3&#xff1a;结论 7.2&#xff1a;Maven介绍 7.3&#xff1a;Maven的优点 Maven安装和配置 7.4&#xff1a;安装教程及环境配置 …

Linux给根目录扩容

需求&#xff1a;Linux系统挂载到根目录的磁盘空间满了&#xff0c;如何扩容&#xff1f; 一、添加磁盘并分区 [rootcdn ~]# fdisk /dev/sdbWelcome to fdisk (util-linux 2.37.2). Changes will remain in memory only, until you decide to write them. Be careful before u…

什么是Java伪随机数,基础打牢。 #程序员 #Java #编程

你一定听说过这样一个词&#xff0c;伪随机数&#xff0c;你有没有这样的疑惑&#xff0c;为什么不用真随机&#xff0c;要用的个假的&#xff1f; 先说一个结论&#xff1a; Java Random英/ˈrndəm/ 随机数生成不安全&#xff0c;如果同时泄漏第一个和第二个随机数&#xf…

uniapp自定义组件

在UniApp中&#xff0c;你可以使用自定义组件来拓展应用程序的功能和界面。自定义组件是由多个Vue组件构成的&#xff0c;可以在应用程序中重复使用。 要创建一个自定义组件&#xff0c;你需要在UniApp项目中的components目录下创建一个新的文件夹&#xff0c;并在该文件夹中创…

最大似然估计的介绍

最大似然估计&#xff08;Maximum Likelihood Estimation&#xff0c;简称MLE&#xff09;是一种用于估计概率分布中参数的方法。该方法的核心思想是选择使得观察到的数据在给定模型下出现的概率最大的参数值作为估计值。 最大似然估计具有很好的性质&#xff0c;包括渐进正态性…

SystemVerilog学习 (9)——随机化

目录 一、概述 二、随机化 2.1、如何简单地产生一个随机数 2.1.1 利用系统函数产生随机数 2.1.2 urandom() 2.2、什么需要随机化 2.3、随机约束 2.3.1 rand 和 randc 2.3.2 随机约束的使用 2.3.3 约束块 三、总结 一、概述 随着设计变得越来越大,要产生一个完整的激…

面试资料快速复习 Git常用命令(简单实用)

Git-command Git常用命令、面试复习、简单实用命令 ​ 一、概念理解 &#xff08;一&#xff09;工作区、暂存区、本地仓库、远程仓库 workspace&#xff1a;工作区staging area&#xff1a;暂存区/缓存区local repository&#xff1a;本地仓库remote repository&#xff…

Apache Airflow (九) :Airflow Operators及案例之BashOperator及调度Shell命令及脚本

&#x1f3e1; 个人主页&#xff1a;IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;加入大数据技术讨论群聊&#xff0c;获取更多大数据资料。 &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你大数据的个人空间-豹…