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

c++类继承

一、继承的规则

(1)基类成员在派生类中的访问权限不得高于继承方式中指定的权限。例如,当继承方式为protected时,那么基类成员在派生类中的访问权限最高也为protected,高于protected会降级为protected,但低于protected不会升级。当继承方式为public时,继承方式则保持不变;

(2)继承方式中的public、protected、private是用来指定基类成员在派生类中最高访问权限的;

(3)不管继承方式是什么,基类中的private成员在派生类中始终不能使用(不能在派生类的成员函数中访问和使用);

(4)如果希望基类的成员既不向外暴漏(不能通过对象访问),还能在派生类中使用,那么只能声明为protected;

在这里插入图片描述

(5)由于private和protected继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以在实际开发中,一般使用public。
(6)在派生类中,可以通过基类的公有成员函数间接访问基类的私有成员;
(7)使用using关键字可以改变基类成员在派生类中的访问权限。

继承访问权限:

#include <iostream>class A
{
public:int m_A = 5;
protected:int m_B = 10;
private:int m_C = 15;
};class B : public A 
{
public:int getB(){return m_B;}
};int main()
{B b;b.m_A = 5;          // 没问题//b.m_B = 10;       // 不能访问,m_B是受保护成员变量//b.m_C = 15;       // 不能访问,m_C是私有变量,派生类不能访问b.getB();           // 可以通过成员函数访问m_B
}

使用using改变继承属性:

#include <iostream>class A
{
public:int m_A = 5;
protected:int m_B = 10;
private:int m_C = 15;
};class B : public A 
{
public:using A::m_B;int getA(){return m_A;}
private:using A::m_A;
};int main()
{B b;// b.m_A = 5;            // 不能访问,使用using m_A变成了私有成员属性b.m_B = 10;              // 没问题,使用using,m_B变成了public//b.m_C = 15;            // 不能访问,m_C是私有变量,派生类不能访问b.getA();               // 可以通过成员函数访问m_A
}

二、类的对象模型

(1)创建派生类对象时,先调用基类构造函数,再调用派生类构造函数;
(2)销毁派生类对象时,先调用派生类析构函数,再调用基类析构函数;
(3)创建派生类对象时只会申请一次内存,派生类对象包含了基类对象的内存空间,this指针相同的;
(4)创建派生类对象时,先初始化基类对象,再初始化派生类对象;
(5)对派生类对象用sizeof得到的是基类所有成员(包括私有成员)+派生类对象所有成员的大小;

#include <iostream>
using namespace std;void* operator new(size_t size)
{void* ptr = malloc(size);   // 申请内存cout << "申请到的内存地址是:" << ptr << " size大小:" << size << endl;return ptr;
}void operator delete(void* ptr)
{if (ptr == nullptr) return;free(ptr);                  // 释放内存cout << "释放了内存。" << endl;
}class A
{
public:int m_A = 5;
protected:int m_B = 10;
private:int m_C = 15;
public:A(){cout << "A中this指针是:" << this << endl;cout << "A中m_A指针是:" << &m_A << endl;cout << "A中m_B指针是:" << &m_B << endl;cout << "A中m_C指针是:" << &m_C << endl;}
};class B : public A
{
public:int m_D = 30;B(){cout << "A中this指针是:" << this << endl;cout << "A中m_A指针是:" << &m_A << endl;cout << "A中m_B指针是:" << &m_B << endl;//cout << "A中m_C指针是:" << &m_C << endl;  // 私有无法访问cout << "A中m_D指针是:" << &m_D << endl;}
};int main()
{cout << "基类占用内存的大小是:" << sizeof(A) << endl;cout << "派生类占用内存的大小是:" << sizeof(B) << endl;B* b = new B;delete b;}

打印输出:

基类占用内存的大小是:12
派生类占用内存的大小是:16
申请到的内存地址是:00000168C69232B0 size大小:16
A中this指针是:00000168C69232B0
A中m_A指针是:00000168C69232B0
A中m_B指针是:00000168C69232B4
A中m_C指针是:00000168C69232B8
A中this指针是:00000168C69232B0
A中m_A指针是:00000168C69232B0
A中m_B指针是:00000168C69232B4
A中m_D指针是:00000168C69232BC
释放了内存。

(6)在C++中,不同继承方式的访问权限只是语法上的处理;
(7)对派生类对象用memset()清空基类私有成员;
(8)用指针可以访问到基类中的私有成员(没有内存对齐,没有占位符)

#include <iostream>
using namespace std;void* operator new(size_t size)
{void* ptr = malloc(size);   // 申请内存cout << "申请到的内存地址是:" << ptr << " size大小:" << size << endl;return ptr;
}void operator delete(void* ptr)
{if (ptr == nullptr) return;free(ptr);                  // 释放内存cout << "释放了内存。" << endl;
}class A
{
public:int m_A = 5;
protected:int m_B = 10;
private:int m_C = 15;
public:A(){cout << "A中this指针是:" << this << endl;cout << "A中m_A指针是:" << &m_A << endl;cout << "A中m_B指针是:" << &m_B << endl;cout << "A中m_C指针是:" << &m_C << endl;}void funcA(){cout << "m_A=" << m_A << ",m_B=" << m_B << ",m_C=" << m_C << endl;}
};class B : public A
{
public:int m_D = 30;B(){cout << "A中this指针是:" << this << endl;cout << "A中m_A指针是:" << &m_A << endl;cout << "A中m_B指针是:" << &m_B << endl;//cout << "A中m_C指针是:" << &m_C << endl;  // 私有无法访问cout << "A中m_D指针是:" << &m_D << endl;}void funcB(){cout << "m_D=" << m_D << endl;}
};int main()
{cout << "基类占用内存的大小是:" << sizeof(A) << endl;cout << "派生类占用内存的大小是:" << sizeof(B) << endl;B* b = new B;b->funcA();  b->funcB();//memset(b, 0, sizeof(B));        // m_A m_B m_C m_D都被清零*((int*)b + 2) = 100;b->funcA();  b->funcB();delete b;}

打印输出:

基类占用内存的大小是:12
派生类占用内存的大小是:16
申请到的内存地址是:00000240B7313490 size大小:16
A中this指针是:00000240B7313490
A中m_A指针是:00000240B7313490
A中m_B指针是:00000240B7313494
A中m_C指针是:00000240B7313498
A中this指针是:00000240B7313490
A中m_A指针是:00000240B7313490
A中m_B指针是:00000240B7313494
A中m_D指针是:00000240B731349C
m_A=5,m_B=10,m_C=15
m_D=30
m_A=5,m_B=10,m_C=100
m_D=30
释放了内存。

三、如何构造基类

(1)创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数;
(2)如果没有指定基类构造函数,将使用基类的默认构造函数;
(3)可以用初始化列表指明要使用的基类构造函数;
(4)基类构造函数负责初始化被继承的数据成员,派生类构造函数主要用于初始化新增的数据成员;
(5)派生类的构造函数总是调用一个基类构造函数,包括拷贝构造函数。

#include <iostream>
using namespace std;class A
{
public:int m_A;
private:int m_B;
public:A():m_A(0), m_B(0) {cout << "调用基类默认构造函数A()" << endl;}A(int a, int b) :m_A(a), m_B(b) {cout << "调用基类构造函数A(int a, int b)" << endl;}A(const A& a) :m_A(a.m_A), m_B(a.m_B) {cout << "调用基类拷贝构造函数A(int a, int b)" << endl;}void funcA(){cout << "m_A=" << m_A << ",m_B=" << m_B << endl;}
};class B : public A
{
public:int m_C = 30;B():m_C(0) {cout << "调用基类默认构造函数B()" << endl;}B(int a, int b, int c) : A(a, b), m_C(c){cout << "调用基类构造函数B(int a, int b, int c)" << endl;}B(const A& a, int c) :A(a), m_C(c) {cout << "调用基类拷贝构造函数B(const A& a, int c)" << endl;}void funcB() {cout << "m_C=" << m_C << endl;}
};int main()
{B b1;                   // 调用基类默认构造函数b1.funcA(); b1.funcB();B b2(1, 2, 3);          // 将调用基类两个参数的构造函数b2.funcA(); b2.funcB();A a(10, 20);B b3(a, 30);b3.funcA(); b3.funcB(); // 调用基类构造函数  
}

打印输出:

调用基类默认构造函数A()
调用基类默认构造函数B()
m_A=0,m_B=0
m_C=0
调用基类构造函数A(int a, int b)
调用基类构造函数B(int a, int b, int c)
m_A=1,m_B=2
m_C=3
调用基类构造函数A(int a, int b)
调用基类拷贝构造函数A(int a, int b)
调用基类拷贝构造函数B(const A& a, int c)
m_A=10,m_B=20
m_C=30

四、名字遮蔽与类作用域

(1)如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,通过派生类对象或者在派生类的成员函数中使用该成员时,将使用派生类新增的成员,而不是基类的;
(2)基类的成员函数和派生类的成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数;
(3)类是一种作用域,每个类都有它自己的作用域,在这个作用域之内定义成员;
(4)在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象引用)来进行访问,静态成员可以通过对象访问,也可以通过类访问;
(5)在成员前面加类名和域解析符可以访问对象的成员;
(6)如果不存在继承关系,域名和域解析符可以省略不写;
(7)当存在继承关系时,基类的作用嵌套派生类的作用域中,如果成员在派生类的作用域已经找到,就不会在基类作用域中继续查找,如果没有找到,则继续在鸡类作用域中查找。

五、继承的特殊关系

(1)如果继承方式是公有的,派生类对象可以使用基类成员;
(2)可以把派生类对象赋值给基类对象(包括私有成员),但是会舍弃非基类的成员;
(3)基类指针可以在不进行显示转换的情况下指向派生类对象;
(4)基类引用可以在不进行显示转换的情况下引用派生类对象。

注意:
(1)基类指针或引用只能调用基类的方法,不能调用派生类的方法;
(2)可以用派生类构造基类;
(3)如果函数的型参是基类,实参可以用派生类;
(4)C++要求指针和引用类型与赋给的类型匹配,这一规则对继承来说是例外。但是,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针(没有价值,没有讨论的必要)。

相关文章:

c++类继承

一、继承的规则 &#xff08;1&#xff09;基类成员在派生类中的访问权限不得高于继承方式中指定的权限。例如&#xff0c;当继承方式为protected时&#xff0c;那么基类成员在派生类中的访问权限最高也为protected&#xff0c;高于protected会降级为protected&#xff0c;但低…...

Git 指令

Git 安装 操作 命令行 简介&#xff1a; Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion …...

JAVA中的多态参数

1.方法定义的参数类型为父类类型&#xff0c;实参类型允许为子类类型 public class Ploy_parameter {public static void main(String[] args) {Manage jack new Manage("jack",12000,3000);Staff tom new Staff("tom",10000);Ploy_parameter ploy_para…...

Ubuntu Linux 下安装和卸载cmake 3.28.2版本

一、安装cmake 1.首先&#xff0c;先从cmake官网下载cmake-3.28.2-linux-x86_64.tar.gz 2.用FinalShell 等文件上传工具&#xff0c;将这个压缩包上传到 虚拟机的某个路径去&#xff08;自选&#xff09; 3. cd /usr/local/bin/&#xff0c;然后创建cmake文件夹&#xff0c;…...

【C++】类和对象3:默认成员函数之析构函数

前言 这篇文章我们来学习默认成员函数中的析构函数 概念 析构函数&#xff1a;与构造函数功能相反&#xff0c;析构函数不是完成对对象本身的销毁&#xff0c;局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数&#xff0c;完成对象中资源的清理工作。 …...

2024美赛C题完整解题教程及代码 网球运动的势头

2024 MCM Problem C: Momentum in Tennis &#xff08;网球运动的势头&#xff09; 注&#xff1a;在网球运动中&#xff0c;"势头"通常指的是比赛中因一系列事件&#xff08;如连续得分&#xff09;而形成的动力或趋势&#xff0c;这可能对比赛结果产生重要影响。球…...

二、人工智能之提示工程(Prompt Engineering)

黑8说 岁月如流水匆匆过&#xff0c;哭一哭笑一笑不用说。 黑8自那次和主任谈话后&#xff0c;对这个“妖怪”继续研究&#xff0c;开始学习OpenAI API&#xff01;关注到了提示工程(Prompt Engineering)的重要性&#xff0c;它包括明确的角色定义、自然语言理解&#xff08;…...

【leetcode题解C++】98.验证二叉搜索树 and 701.二叉搜索树中的插入操作

98. 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例…...

【Vue.js设计与实现】第二篇:响应系统-阅读笔记(持续更新)

从高层设计的角度去探讨框架需要关注的问题。 系列目录&#xff1a; 标题博客第一篇&#xff1a;框架设计概览【Vue.js设计与实现】第一篇&#xff1a;框架设计概览-阅读笔记第二篇&#xff1a;响应系统【Vue.js设计与实现】第二篇&#xff1a;响应系统-阅读笔记第三篇&#x…...

微信小程序之本地生活案例的实现

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…...

智能决策的艺术:探索商业分析的最佳工具和方法

文章目录 一、引言二、商业分析思维概述三、数据分析在商业实践中的应用四、如何培养商业分析思维与实践能力五、结论《商业分析思维与实践&#xff1a;用数据分析解决商业问题》亮点内容简介作者简介目录获取方式 一、引言 随着大数据时代的来临&#xff0c;商业分析思维与实…...

C#(C Sharp)学习笔记_前言及Visual Studio Code配置C#运行环境【一】

前言 这可以说是我第一次正式的踏入C#的学习道路&#xff0c;我真没想过我两年前是怎么跳过C#去学Unity3D游戏开发的&#xff08;当然了&#xff0c;游戏开发肯定是没有成功的&#xff0c;都是照搬代码&#xff09;。而现在&#xff0c;我真正地学习一下C#&#xff0c;就和去年…...

政安晨的AI笔记——Bard大模型最新提示词创作绘画分析

AI大模型进入商业应用元年后的第一年&#xff0c;顶级模型大混战终于开始了。 Bard在追赶OpenAI的过程中&#xff0c;还是补上了画图的短板。 &#xff08;相比于视频的5阶张量处理而言&#xff0c;图画做为4阶张量处理虽然不新鲜&#xff0c;但却是跨不过去的基础条件&#…...

基础算法bfs -剪枝问题

问题描述:一个迷宫有 NXM 格,有一些格子是地板,能走;有一些格子是障碍,不能走。给一个起点S和一个终点D。一只小狗从 S出发,每步走一块地板&#xff0c;在每块地员不能停留&#xff0c;而且走过的地板都不能再走。给定一个 T,问小狗能正好走 T步到达D吗?输入:有很多测试样例。…...

在Meteor Lake上测试基于Stable Diffusion的AI应用

上个月刚刚推出的英特尔新一代Meteor Lake CPU&#xff0c;预示着AI PC的新时代到来。AI PC可以不依赖服务器直接在PC端处理AI推理工作负载&#xff0c;例如生成图像或转录音频。这些芯片的正式名称为Intel Core Ultra处理器&#xff0c;是首款配备专门用于处理人工智能任务的 …...

情人节心动礼物:共度情人节美好时刻的礼物推荐

情人节&#xff0c;这个充满浪漫与爱意的特殊日子&#xff0c;总是让人心跳加速&#xff0c;期待着与爱人共享甜蜜时光。在这一天&#xff0c;送出一份精心挑选的礼物&#xff0c;不仅能够表达你对另一半无尽的爱意&#xff0c;更能让这份爱升华&#xff0c;成为你们爱情故事中…...

远程手机搭建Termux环境,并通过ssh连接Termux

背景 Termux只能通过鼠标点击&#xff0c;无法使用电脑键盘&#xff0c;输入速度很慢&#xff0c;你想通过ssh 连接Termux&#xff0c;获得友好体验搞了个云手机&#xff0c;想像普通手机那样充当服务器想把自己的手机公开到局域网中供同事调试想把自己的模拟器公开到局域网中…...

基于EdgeWorkers的边缘应用如何进行单元测试?

随着各行各业数字化转型的持续深入&#xff0c;越来越多企业开始选择将一些应用程序放在距离最终用户更近的边缘位置来运行&#xff0c;借此降低延迟&#xff0c;提高应用程序响应速度&#xff0c;打造更出色的用户体验。 相比传统集中部署和运行的方式&#xff0c;这种边缘应…...

【linux】校招中的“熟悉linux操作系统”一般是指达到什么程度?

这样&#xff0c;你先在网上找一套完整openssh升级方案&#xff08;不是yum或apt的&#xff0c;要源码安装的&#xff09;&#xff0c;然后在虚拟机上反复安装测试&#xff0c;直到把他理解了、背下来。 面试的时候让你简单说说linux命令什么的&#xff0c;你就直接把这个方案…...

【CSS系列】常用容易忽略的css

user-select user-select 是一个 CSS 属性&#xff0c;用于控制用户是否可以选择文本。通过设置 user-select 的值&#xff0c;可以决定用户是否可以选择元素中的文本&#xff0c;以及如何选择文本。 auto&#xff1a;默认值。浏览器可以选择文本。none&#xff1a;用户不能选…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...