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

240607 继承

面向对象三大特性:封装、继承、多态

RE: 封装

  1. C++把数据和方法封装在类里面
  2. 迭代器和适配器

继承

1 基类 & 派生类

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

class 派生类(子类): 访问修饰符 基类(父类)注:访问修饰符是 publicprotectedprivate 其中的一个,未使用它则默认是 private

假设有一个基类 Shape,Rectangle 是它的派生类,如下所示:

#include <iostream>
using namespace std;// 基类
class Shape 
{public:void setWidth(int w){width = w;}void setHeight(int h){height = h;}protected:int width;int height;
};// 派生类
class Rectangle: public Shape
{public:int getArea(){ return (width * height); }
};int main(void)
{Rectangle Rect;Rect.setWidth(5);Rect.setHeight(7);// 输出对象的面积cout << "Total area: " << Rect.getArea() << endl;return 0;
}

2 访问控制和继承

访问			public	 protected	 private
同一个类		yes		 yes		 yes
派生类		yes		 yes		 no
外部的类		yes		 no			 no

3 赋值兼容转换(切片)

派生类对象 可以赋值给 基类的对象/基类的指针/基类的引用
(切片:把派生类中基类那部分切来赋值过去) 赋值语句:父类对象 = 子类对象
基类对象不能赋值给派生类对象
注:单独的语法规则,不是类型转换,没有产生临时变量,与下述引例机制不同

引例:截断和提升

// 类型转换会产生临时变量
int i = 1234;
printf("%x\n", i);// 4d2
// 截断
char ch = i;
printf("%x\n", ch);// ffffffd2
// 提升
i = ch;
printf("%x\n", i);// ffffffd2const int& ref_i = i;// 临时变量具有常性
printf("%d\n", ref_i);// -46const char& ref_ch = ch;// 临时变量具有常性
printf("%d\n", ref_ch);// -46

1234 的二进制表示为:10011010010(除 2 取余法)
补全至16位:0000 0100 1101 0010
然后,将每一组二进制转换为十六进制:
0000 = 0
0100 = 4
1101 = D(大小写皆可)
0010 = 2
最终组合成十六进制:4d2

第一个输出结果:4d2

截断
因为 char 类型只占用 1 个字节(8 位),而 i 是一个 32 位的整数,在赋值给 char 时,只保留了最低 8 位的内容(1101 0010,即 0xd2),其余高位被截断。
留下的内容最高位是 1,表明这是一个负数,当 char 被提升为 int 以打印时,会进行符号扩展,高位会被填充为 1,使得 ch 变成 0xFFFFFFD2(在 32 位系统上)。

第二个输出结果:ffffffd2

提升:

ch 是有符号类型且其值为 1101 0010,
找到补码:当前的二进制数 11010010 就是补码。
求反码(将所有位取反):11010010 取反后得到:00101101
加 1:00101101 + 1 = 00101110
这个结果是 00101110,对应的十进制值是 46。
因为符号位是 1,表示这是一个负数,所以最终值为:-46。

ch 是有符号类型且其值为 0xD2(-46 in decimal),类型提升时高位会填充符号位,因此 i 的值为 0xFFFFFFD2

第三个输出结果:ffffffd2

// 赋值兼容转换
Shape Sh1;
Rect1.name = "RECT";
Sh1 = Rect1;
Shape* ptr = &Sh1;
Shape& ref = Sh1;
ptr->name += "x";
ref.name += "x";
Rect1.PrintName();
Sh1.PrintName();
cout << endl;

两次打印结果如下:

RECT
RECTxx

对象的赋值是拷贝赋值;
Rect1 和 Sh1 是两个独立的对象: 由于 Sh1 是 Rect1 的副本,对 Sh1 的修改不会影响 Rect1,反之亦然。

4 继承的作用域

各作用域的影响:

作用域语法编译查找规则生命周期
局部域
全局域
命名空间域
(默认不查找,除非展开或指定)
不存在
类域不存在
4.1 示例1
class Person
{
protected:string _name = "小李子"; // 姓名int _num = 111; // 身份证号
};class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << "学号:" << _num << endl;// Student的_num和Person的_num构成隐藏关系cout << "身份证号:" << Person::_num << endl;}
protected:int _num = 999; // 学号
};void TestStu()
{Student s1;s1.Print();
};

姓名:小李子
学号:999
身份证号:111

4.2 示例2
class A {
public:void ft() {cout << "void ft()" << endl;}
};class B :public A {
public:void ft(int i) {A::ft();cout << "void ft(int i), i = " << i << endl;}
};void TestB() {B b;b.ft(1);// Q:重载,隐藏,编译报错,运行报错?// A:两者构成隐藏,函数重载的前提是在同一个作用域
}

void ft()
void ft(int i), i = 1

变式

class A {
public:void ft() {cout << "void ft()" << endl;}
};class B :public A {
public:void ft(int i) {cout << "void ft(int i), i = " << i << endl;}
};void TestB() {B bb;bb.ft();// Q:重载,隐藏,重写,编译报错,运行报错?(不定项选择)// A:两者构成隐藏且编译报错// 如何调用父类?b2.A::ft();
}

5 继承过程中涉及的构造、拷贝构造和析构函数的工作机制

5.1 构造函数在继承中的作用和调用顺序
5.1.1 构造函数的调用顺序

当创建派生类对象时,基类的构造函数会先于派生类的构造函数被调用。这是因为派生类需要依赖基类的成员和功能,所以必须先初始化基类部分。
在构造派生类对象时,不能先初始化派生类再初始化基类,因为派生类的构造函数可能依赖于基类的成员。如果基类未先初始化,这些成员将包含随机值,从而导致不确定的行为。

示例:

#include <iostream>
using namespace std;class Base {
public:Base() {cout << "Base Default Constructor" << endl;}
};class Derived : public Base {
public:Derived() {cout << "Derived Default Constructor" << endl;}
};int main() {Derived d;return 0;
}

输出:

Base Default Constructor
Derived Default Constructor

解释:

  • 基类成员当成一个整体:在构造派生类对象时,基类部分被当作一个整体,调用其默认构造函数来初始化。
  • 派生类自己的成员
    • 内置类型成员:根据编译器的实现,可能会自动初始化(如置零),也可能不处理,产生未定义的值。
    • 自定义类型成员:会调用它们的默认构造函数进行初始化。
5.1.2 派生类构造函数如何初始化基类

如果基类没有默认构造函数,或者需要传递参数,可以在派生类的构造函数的初始化列表中显式调用基类的构造函数。

示例:

class Base {
public:int baseValue;Base(int x) : baseValue(x) {cout << "Base Parameterized Constructor" << endl;}
};class Derived : public Base {
public:int derivedValue;Derived(int x, int y) : Base(x), derivedValue(y) {cout << "Derived Parameterized Constructor" << endl;}
};

解释:

  • 派生类的构造函数在初始化列表中调用了基类的构造函数 Base(x),并初始化了自己的成员 derivedValue(y)

5.2 拷贝构造函数在继承中的行为
5.2.1 默认拷贝构造函数的生成

当你没有显式定义拷贝构造函数时,编译器会为你生成一个默认拷贝构造函数。对于派生类,默认拷贝构造函数的行为如下:

  • 基类成员当成一个整体:调用基类的拷贝构造函数来复制基类部分的数据。
  • 派生类自己的成员
    • 内置类型成员:逐个成员进行值拷贝(浅拷贝)。
    • 自定义类型成员:调用它们的拷贝构造函数进行复制。

示例:

class Base {
public:int baseValue;Base(int x) : baseValue(x) {}Base(const Base& other) : baseValue(other.baseValue) {cout << "Base Copy Constructor" << endl;}
};class Derived : public Base {
public:int* derivedValue;Derived(int x, int y) : Base(x) {derivedValue = new int(y);}// 默认拷贝构造函数// Derived(const Derived& other) : Base(other), derivedValue(other.derivedValue) {}~Derived() {delete derivedValue;}
};

当我们执行以下代码:

Derived d1(10, 20);
Derived d2 = d1; // 调用默认拷贝构造函数

可能的问题:

  • derivedValue 是一个指针,默认拷贝构造函数会进行浅拷贝,即复制指针的值。
  • 这会导致 d1d2derivedValue 指向同一块内存,可能在析构时造成重复释放(double free)等错误。
5.2.2 需要自定义拷贝构造函数的情况

当派生类的成员涉及到动态内存分配或需要深拷贝时,必须自定义拷贝构造函数。

示例(自定义拷贝构造函数):

class Derived : public Base {
public:int* derivedValue;Derived(int x, int y) : Base(x) {derivedValue = new int(y);}Derived(const Derived& other) : Base(other) { // 调用基类的拷贝构造函数derivedValue = new int(*other.derivedValue); // 深拷贝cout << "Derived Copy Constructor" << endl;}~Derived() {delete derivedValue;}
};

解释:

  • 基类成员当成一个整体:在派生类的拷贝构造函数中,显式调用了基类的拷贝构造函数 Base(other)
  • 派生类自己的成员
    • 内置类型成员:如果有内置类型成员,默认会进行值拷贝。
    • 自定义类型成员:需要手动编写代码来实现深拷贝,防止多个对象共享同一块内存。

5.3 析构函数在继承中的作用和调用顺序
5.3.1 析构函数的调用顺序

当销毁派生类对象时,析构函数的调用顺序与构造函数相反

  1. 首先调用派生类的析构函数,清理派生类特有的资源。
  2. 然后调用基类的析构函数,清理基类部分的资源。

示例:

class Base {
public:~Base() {cout << "Base Destructor" << endl;}
};class Derived : public Base {
public:~Derived() {cout << "Derived Destructor" << endl;}
};int main() {Derived d;return 0;
}

输出:

Derived Destructor
Base Destructor

解释:

  • 先销毁派生类部分,释放派生类特有的资源。
  • 然后销毁基类部分,确保对象的所有资源都被正确释放。
5.3.2 虚析构函数的重要性

在涉及多态的情况下,如果你通过基类指针删除派生类对象,基类的析构函数必须是虚函数(virtual,否则可能导致派生类的析构函数不被调用,造成资源泄漏。

示例:

class Base {
public:virtual ~Base() {cout << "Base Destructor" << endl;}
};class Derived : public Base {
public:~Derived() {cout << "Derived Destructor" << endl;}
};int main() {Base* ptr = new Derived();delete ptr; // 正确调用派生类和基类的析构函数return 0;
}

输出:

Derived Destructor
Base Destructor

解释:

  • 基类的析构函数被声明为虚函数后,delete 基类指针时,会先调用派生类的析构函数,再调用基类的析构函数。
  • 如果基类析构函数不是虚函数,只会调用基类的析构函数,派生类的资源可能得不到释放。

5.4 总结与注意事项
5.4.1 默认构造函数的行为
  • 基类成员:在派生类的构造过程中,基类部分被当作一个整体,调用基类的默认构造函数。
  • 派生类的内置类型成员:编译器可能会自动初始化(如置零),也可能不处理,这取决于编译器实现。
  • 派生类的自定义类型成员:会调用它们的默认构造函数进行初始化。
5.4.2 默认拷贝构造函数的行为
  • 基类成员:调用基类的拷贝构造函数,复制基类部分的数据。
  • 派生类的内置类型成员:逐个成员进行值拷贝(浅拷贝)。
  • 派生类的自定义类型成员:调用它们的拷贝构造函数。
5.4.3 何时需要自定义拷贝构造函数
  • 当派生类的成员涉及到动态内存分配、文件句柄、网络连接等需要深拷贝的资源时,必须自定义拷贝构造函数和赋值运算符,以正确管理资源,防止浅拷贝带来的问题。
5.4.4 赋值操作符的注意事项
  • 类似于拷贝构造函数,赋值操作符在默认情况下也会进行浅拷贝。如果涉及到需要深拷贝的成员,应该自定义赋值操作符。
5.4.5 避免资源泄漏和悬垂指针
  • 正确地管理对象的生命周期,确保析构函数能被正确调用,防止资源泄漏。
  • 注意浅拷贝带来的悬垂指针问题(指针指向已被释放的内存)。

相关文章:

240607 继承

面向对象三大特性&#xff1a;封装、继承、多态 RE: 封装 C把数据和方法封装在类里面迭代器和适配器 继承 1 基类 & 派生类 一个类可以派生自多个类&#xff0c;这意味着&#xff0c;它可以从多个基类继承数据和函数。定义一个派生类&#xff0c;我们使用一个类派生列表…...

轻松应对意外丢失:高效电脑数据恢复指南!

有时候由于误操作、硬件故障、病毒攻击等原因&#xff0c;电脑里的重要文件可能会突然消失不见。面对这样的情况&#xff0c;很多人会感到手足无措。其实&#xff0c;借助专业的电脑数据恢复软件&#xff0c;我们可以较为轻松地找回丢失的数据。今天&#xff0c;我们就来介绍几…...

vue项目中播放rtsp视频流

一、下载webrtc-streamer 下载地址&#xff1a;https://github.com/mpromonet/webrtc-streamer/releases 根据设备型号下载对应的版本到本地直接解压就行&#xff0c;我下载的是webrtc-streamer-v0.8.6-dirty-Windows-AMD64-Release.tar版本。 双击webrtc-streamer.exe可执行文…...

tomcat部署web配置环境变量

在Tomcat中设置环境变量通常涉及以下步骤&#xff1a; 找到Tomcat的启动脚本&#xff08;如catalina.sh或catalina.bat&#xff09;。 在启动脚本中设置环境变量。 对于catalina.sh&#xff08;Linux/Unix系统&#xff09;&#xff0c;你可以在文件顶部添加环境变量&#xf…...

数据仓库技术及应用(练习1)

1.创表 &#xff08;1&#xff09;customers.csv CREATE EXTERNAL TABLE IF NOT EXISTS customers ( customer_id int, customer_fname varchar(45), customer_lname varchar(45), customer_email varchar(45), customer_password varchar(45), customer_street …...

老板的“神助攻”:公司电脑监控软件

在当今的商业世界中&#xff0c;企业管理者都希望员工能全身心投入工作&#xff0c;为企业创造更多价值。然而&#xff0c;员工上班摸鱼的现象却让许多老板头疼不已。公司电脑监控软件的出现&#xff0c;为解决这一问题提供了可能。接下来&#xff0c;我们将详细介绍几款优质的…...

前端vue部署网站

这里讲解一下前端vue框架部署网站&#xff0c;使用工具是 xshell 和 xftp &#xff08;大家去官网安装免费版的就行了&#xff09; 服务器 我使用的阿里云服务器&#xff0c;买的是 99 一年的&#xff0c;淘宝有新手9.9 一个月服务器。可以去用&#xff0c;学生的话是有免费三…...

Unity3D 动画回调函数详解

在Unity3D中&#xff0c;动画回调函数是实现精细动画效果的重要工具。通过动画回调函数&#xff0c;我们可以在动画的特定时刻执行自定义代码&#xff0c;从而实现更加灵活和复杂的动画效果。本文将详细解释Unity3D中的动画回调函数&#xff0c;并提供相应的代码实现。 对惹&a…...

el-table表格表尾合计行,指定合计某几列,自定义合计方法

&#x1f935; 作者&#xff1a;coderYYY &#x1f9d1; 个人简介&#xff1a;前端程序媛&#xff0c;目前主攻web前端&#xff0c;后端辅助&#xff0c;其他技术知识也会偶尔分享&#x1f340;欢迎和我一起交流&#xff01;&#x1f680;&#xff08;评论和私信一般会回&#…...

一款工具替你解决Mac电脑菜单栏图标杂乱问题

你的菜单栏是不是各种图标挤在一起&#xff1f;图标过多显得杂乱&#xff1f;刘海屏遮挡菜单栏图标&#xff1f;教你如何让你的菜单栏变的简洁美观 iBar&#xff0c;一款Mac上优秀的菜单栏管理工具&#xff0c;可以自主选择菜单栏图标隐藏&#xff0c;单独窗口聚合展示&#x…...

MySQL 基础入门教程

参考视频地址&#xff1a;一小时MySQL教程 bilibili SQL 基础 数据库分为关系型数据库和非关系型数据库 常见的关系型数据库&#xff1a; MySQL、PostgreSQL、Oracle、SQL Server等。 非关系型数据库&#xff1a; MongoDB&#xff08;文档型数据库&#xff09;、Redis&am…...

俏生元将传统膳食智慧融入现代生活,自然成分绽放健康光彩

近年来&#xff0c;当代女性健康食品市场正经历快速发展和显著变化。随着女性健康意识的提升&#xff0c;市场对专门针对女性健康的产品需求快速上升。女性消费者对健康的关注不再局限于表面&#xff0c;而是越来越注重内在健康和生活质量的提升。此外&#xff0c;中式养生文化…...

腾讯云推流播放相关

直播的在线人数是否有上限&#xff1f; 腾讯云直播默认不限制观看直播的在线人数&#xff0c;只要网络等条件允许都可以观看直播。如果用户配置了带宽限制&#xff0c;当观看人数过多、超出了限制带宽时新的用户无法观看&#xff0c;此情况下在线人数是有限制的。 如何使用播…...

UE5运行时动态加载场景角色动画任意搭配-相机及运镜(二)

通过《MMD模型及动作一键完美导入UE5》系列文章,我们可以把外部场景、角色、动画资产导入UE5,接下来我们将实现运行时动态加载这些资产,并任意组合搭配。 1、运行时播放相机动画 1、创建1个BlueprintActor,通过这个蓝图动态创建1个LevelSequence,并Play 2、将这个Bluep…...

@JsonAlias和@JSONField序列化和反序列化

com.fasterxml.jackson.annotation.JsonAlias("expressCode") com.alibaba.fastjson.annotation.JSONField(name "expressCode") 这两个注解分别属于不同的JSON序列化框架&#xff1a;Jackson 和 Fastjson&#xff0c;它们的用途是处理JSON字段的名称映射…...

k8s1.27部署ingress 1.11.2

k8s1.27部署ingress 1.11.2 要求&#xff1a; 1、使用主机网络。 2、多节点部署&#xff0c;以来标签&#xff1a;isingressistrue ingress1.11.2支持版本 官方参考链接&#xff1a; https://github.com/kubernetes/ingress-nginx/ 官网yaml https://raw.githubuserconten…...

【运维】自动化运维详解

目录 引言一、什么是自动化运维&#xff1f;二、自动化运维的优势三、自动化运维的关键组成部分详解3.1 监控与告警3.2 部署与配置管理3.3 备份与恢复3.4 安全管理 总结 引言 在当今信息技术飞速发展的时代&#xff0c;企业对IT基础设施的依赖日益增强&#xff0c;传统的人工运…...

线控底盘技术介绍

随着汽车工业的不断发展&#xff0c;传统的机械控制系统逐渐向电子控制系统转变。线控底盘&#xff08;Drive-by-Wire Chassis&#xff09;作为这一转变的重要组成部分&#xff0c;正在改变汽车的操控方式和驾驶体验。本文将全面介绍线控底盘的概念、组成、工作原理、优缺点、应…...

DOM对象

DOM概述 官方定义&#xff1a; DOM是W3C制定的一个规范(标准)&#xff0c;(Document Object Model&#xff0c;文档对象模型)&#xff0c;是提供了访问和操作网页中各元素的方法&#xff0c;让程序可以动态的修改或改变网页元素的内容、样式、结构。 DOM是W3C制定的一个规范…...

[SQL] 数据库图形化安装和使用

一 安装 1.1 图形化安装 下载DataGrip安装包 点击此处一直下一步即可。点击免费使用。 进去界面后,选择新建一个项目 点击加号&#xff0c;创建一个Mysql连接。输入Mysql的连接信息。点击DownLoad下载Mysql的驱动 接下来点击创建的mysq项目中后面的三个点&#xff0c;选择…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...