当前位置: 首页 > 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;用户不能选…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...