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++要求指针和引用类型与赋给的类型匹配,这一规则对继承来说是例外。但是,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针(没有价值,没有讨论的必要)。

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

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

相关文章

通讯基本概念

通信的方式有多种&#xff0c;按数据传输方式可分为串行通讯和并行通信&#xff1b;按通信数据同步方式可分为同步通信和异步通信&#xff1b;按数据通信的方向可分为 一、串行通信和并行通信 串行通信&#xff1a;设备之间通过少量的数据信号线&#xff08;一般是8根以下&am…

STM32 自学笔记 学习笔记 一

起源&#xff0c;A7,A9,M3&#xff0c;原来弄了A9的TQ2440&#xff0c;结果还得来重新熟悉下32函数JLINK使用SW方式&#xff0c;本来可以下载&#xff0c;但是一根线掉了重新上去&#xff0c;就出各种跟线无关问题&#xff0c;干脆把32断了重新接&#xff0c;结果就成功了&…

Linux权限【超详细】

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 扩展知识&#xff1a…

初识webpack(一)概念、入口配置、输出配置、loader等

目录 (一)概念 webpack的依赖图 (二)webpack的基本使用 (三)webpack的配置文件 1.入口(entry)配置 2.输出(output)配置 (三)loader 1.css文件处理 (1)安装css-loader和style-loader (2)在webpack.config.js中配置loader 2.less文件处理 3.postcss的使用 (1)安装…

深入理解K均值算法:Python中的应用与实践

目录 写在开头1. K均值算法基础1.1 什么是K均值算法&#xff1f;1.2 K均值算法的工作原理1.3 算法的优势与局限性 2. K均值算法的实现步骤2.1 初始聚类中心的选择方法2.1.1 随机选择初始中心点2.1.2 K均值算法 2.2 数据点与聚类中心的距离计算2.2.1 欧氏距离计算2.2.2 曼哈顿距…

MySQL-----约束

目录​​​​​ 约束 一 主键约束 1-1 操作-添加单列主键 1-2 操作-添加多列主键 1-3 修改表结构添加主键 1-4 删除主键约束 二 自增长约束 2-1 指定自增长字段的初始值 2-2 删除自增列 三 非空约束 3-1 创建非空约束 3-2 删除非空约束 四 唯一约束…

寒假作业2月4号

第三章 类与构造函数 一&#xff0e;选择题 1、下列不能作为类的成员的是&#xff08;B&#xff09; A. 自身类对象的指针 B. 自身类对象 C. 自身类对象的引用 D. 另一个类的对象 2、假定AA为一个类&#xff0c;a()为该类公有的函数成员&#xff0c;x为该类的一个对象&am…

SpringCloud + Nacos环境下抽取Feign独立模块并支持MultipartFile

文章目录 一、前提条件和背景1. 前提2. 背景 二、Feign模块1. 依赖引入2. application.yaml配置3. 扩展支持MultipartFile4. 将media-api注册到feign 三、Media模块四、Content模块1. 引入依赖2. 启用FeignClient3. 测试 五、需要澄清的几点 一、前提条件和背景 1. 前提 已经…

Axure RP9原型设计工具使用记录:实际应用及问题记录

Axure RP9使用记录二 &#x1f4da;第三章 实际应用&#x1f4d7;快速归位00坐标&#x1f4d7;动态菜单&#x1f4d7;填充图片&#x1f4d7;下拉框联动&#x1f4d7;单选框&#x1f4d7;全局变量 ⁉️问题记录❓问题一&#xff1a;菜单不显示❗解决方式&#xff1a;调整菜单元件…

深入Spring MVC的工作流程

深入Spring MVC的工作流程 在Spring MVC的面试问题中&#xff0c;常常被询问到的一个问题。Spring MVC的程序中&#xff0c;HTTP请求是如何从开始到结束被处理的。为了研究这个问题&#xff0c;我们将需要深入学习一下Spring MVC框架的核心过程和工作流程。 1. 启动请求生命周…

【UE5 C++】超详细虚幻C++零基础学习教程

B站免费教程&#xff0c;虚幻C零基础教学入门级视频&#xff0c;帮助大家学习虚幻C。 视频地址&#xff1a;【虚幻5】UE5C零基础全网全流程开发从入门到进阶教程合集&#xff08;持续跟新中&#xff09;_哔哩哔哩_bilibili 课程介绍视频如下 【虚幻5】UE5C零基础全网全流程开…

[python]基于LSTR车道线实时检测onnx部署

【框架地址】 https://github.com/liuruijin17/LSTR 【LSTR算法介绍】 LSTR车道线检测算法是一种用于识别和定位车道线的计算机视觉算法。它基于图像处理和机器学习的技术&#xff0c;通过对道路图像进行分析和处理&#xff0c;提取出车道线的位置和方向等信息。 LSTR车道线…

Qt之使用Qt内置图标

一效果 二.原理 Qt内置图标封装在QStyle中,共七十多个图标,可以直接拿来用,能应付不少简单程序需求,不用自己去找图标并添加到资源文件了。 下面是内置图标的枚举定义: enum StandardPixmap {SP_TitleBarMenuButton,SP_TitleBarMinButton,SP_TitleBarMaxButton,SP_T…

PHP框架详解 - symfony框架

首先说一下为什么要写symfony框架&#xff0c;这个框架也属于PHP的一个框架&#xff0c;小编接触也是3年前&#xff0c;原因是小编接触Golang&#xff0c;发现symfony框架有PHP框架的东西也有Golang的东西&#xff0c;所以决定总结一下&#xff0c;有需要的同学可以参看小编的G…

【数据结构】链表OJ面试题(题库+解析)

前言 还不清楚链表的码喵们可以看看前篇关于链表的详解 http://t.csdnimg.cn/X6t6P 1.链表面试题 既然已经懂得了链表该如何实现&#xff0c;那么现在就趁热打铁开始练习&#xff01;这里给码喵们整理了相对不错的一些OJ题来练习 1. 删除链表中等于给定值 val 的所有结点。 力…

【Lambda表达式和函数式接口】

目录 Lambda表达式和函数式接口的使用具有以下几个影响&#xff1a;下面是一个简单的示例代码&#xff0c;使用Lambda表达式实现一个对列表进行遍历的操作&#xff1a; 在Java 8及以上版本中&#xff0c;Lambda表达式是一种函数式编程的特性&#xff0c;它可以使代码更加简洁、…

【Tomcat与网络2】一文理解Servlet是怎么工作的

在前面&#xff0c;我们研究了如何用idea来启动一个Servlet程序&#xff0c;今天我们就再来看一下Servlet是如何工作的。 目录 1.Servlet 介绍 2.Servlet 容器工作过程 3.Servlet的扩展 不管是电脑还是手机浏览器&#xff0c;发给服务端的就是一个 HTTP 格式的请求&#xf…

微信网页授权之使用完整服务解决方案

目录 微信网页授权能力调整造成的问题 能力调整的内容和理由 原有运行方案 is_snapshotuser字段 改造原有方案 如何复现测试场景 小结 微信网页授权能力调整造成的问题 依附于第三方的开发&#xff0c;做为开发者经常会遇到第三方进行规范和开发的调整&#xff0c;如开…

【EI会议征稿通知】2024年材料物理与复合材料国际学术会议

2024年材料物理与复合材料国际学术会议 2024 International Conference on Materials Physics and Composites(ICMPC 2024) 2024年材料物理与复合材料国际学术会&#xff08;ICMPC 2024&#xff09;将于2024年5月24-26日在中国成都举行。ICMPC 2024将吸引顶尖的研究人员和从业…

Python flask 模板详解

文章目录 1 概述1.1 模板简介1.2 templates 文件1.3 简单应用 2 模板语法2.1 for 循环2.2 if 判断 3 模板的继承3.1 格式要求3.2 实现示例3.3 复用父模板的内容&#xff1a;super 1 概述 1.1 模板简介 定义&#xff1a;定义好的 html 文件&#xff0c;用于快速开发 web 页面J…