C++学习笔记----9、发现继承的技巧(三)---- 尊重父类(1)
当写继承类的时候,需要清楚父类与子类之间的交互。像生成顺序,构造函数链,以及转化都可以是问题的根源。
1、父类构造函数
对象不会马上就能干活;它们必须由父类以及所包含的任意对象进行构建。c++定义了如下的生成顺序:
- 、如果类有基类,基类的缺省构造函数先执行,除非在构造函数生成器中有对基类的调用,在这种情况下,生成器先调用而不是缺省构造函数。
- 、类的Non-static数据成员以它们声明的顺序构建。
- 、类的构造函数体执行。
这些规则可以递归应用。如果类有一个祖父类,祖父类先于父类初始化,等等。下面的代码展示了这种生成次序。该代码正确执行的输出为123。
import std;using namespace std;class Something
{
public:Something() { print("2"); }
};class Base
{
public:Base() { print("1"); }
};class Derived : public Base
{
public:Derived() { print("3"); }private:Something m_dataMember;
};int main()
{Derived myDerived;
}
当myDerived对象生成的时候,Base的构造函数先调用,输出字符串“1”。接下来,m_dataMember初始化,调用Something构造函数,它输出字符串“2”。最后,Derived构造函数调用,输出“3”。
注意Base的构造函数是自动调用的。c++自动调用父类有的缺省构造函数。如果父类没有缺省构造函数或者有但是你想用另外的父类构造函数,你可以在构造函数初始化器内初始化数据成员时chain构造函数。例如,下面的代码展示了Base的缺省缺省构造函数的版本。Derived的相关版本必须显式告诉编译器如何 调用Base构造函数,要么代码就会编译失败。
class Base
{
public:explicit Base(int i) {}
};class Derived : public Base
{
public:Derived() : Base { 7 } { /* Other Derived's initialization ... */ }
};
Derived构造函数传了一个固定值(7)给Base构造函数。当然了,Derived也可以传递一个变量:
Derived::Derived(int i) : Base { i } { /* Other Derived's initialization ... */ }
从继承类中传递构造函数变量给基类是完全没有问题的,也是很正常的。然则传递数据成员是不行的。代码编译没有问题,但是记住数据成员是在基类构建完成之后才会初始化的。如果作为参数传递一个数据成员给父类构造函数,它是没有被初始化的。
2、父类析构函数
因为析构函数不接受参数,编程语言会自动调用父类的析构函数。析构函数的调用次序与构造函数的次序刚好相反:
- 、调用类的析构函数体。
- 、类的数据成员以构建相反的次序依次失效。
- 、如果有父类的话,被析构。
再次声明,该规则可以递归。链条最低的成员首先失效。下面的代码给上面的例子添加了析构函数。析构函数都声明成了virtual!执行的话,代码输出“123321”。
class Something
{
public:Something() { print("2"); }virtual ~Something() { print("2"); }
};class Base
{
public:Base() { print("1"); }virtual ~Base() { print("1"); }
};class Derived : public Base
{
public:Derived() { print("3"); }virtual ~Derived() override { print("3"); }private:Something m_dataMember;
};
如果上面的析构函数不是被声明为virtual,代码看起来也能正常运行。然而,如果 调用Base指针上的delete,而该指针是指向一个Derived实例的话,析构链条就不对了。例如,如果将上面代码的所有析构函数的virtual与override关键字移除的话,当Derived对象作为一个指向Base的对象来访问并且删除的话就会有问题出现了,如下所示:
Base* ptr { new Derived{} };
delete ptr;
该代码的输出会是令人吃惊的“1231”。当ptr指针删除时,只有Base析构函数被调用,因为该析构函数没有被声明为virtual。作为结果,Derived析构函数没有被调用,其数据成员的析构函数也没有被调用!
从技术上来说,改正前面的问题只需要将Base的析构函数标记为virtual。其”virtualness”属性自动应用于任何它的继承类。然而,我强烈建议你显式将所有的析构函数标记为virtual,这样就永远不需要担心这方面的问题。
警告:总是把析构函数标记为virtual!编译器生成的缺省析构函数不是virtual,所以应该定义(或显式缺省)一个virtual析构函数,至少对于所有的non-final基类。
3、在构造函数也析构函数中调用virtual成员函数
在构造函数与析构函数中virtual成员函数表现是不一样的。如果继承类一从基类重载来的virtual成员函数,从基类构造函数或析构函数调用该成员函数调用的是基类的virtual成员函数而不是继承类中的重载版本!换名话说,在构造函数或析构函数中对virtual成员函数的调用是在编译时静态解析的。
构造函数的这种行为的原因与构建一个继承类的实例时的初始化顺序相关。当生成这样的实例时,任何基类的构造函数先调用,在继承类实例整体初始化之前。因此,去调用还没有初始化的继承类中重载的virtual成员函数是危险的。对于析构函数也是类似的原因,也是当析构一个对象时其析构的顺序。
如果确实需要对构造函数进行行为多态化,虽然不是很推荐,也是可以在基类中定义一个initialize() virtual成员函数的,它可以被继承类重载。客户生成类实例,在构建完成后一定要调用这个initialize()成员函数。
同样的,如果需要在析构函数中使行为多态化,再次强调,是不推荐的,可以定义一个shutdown() virtual成员函数,对象在析构之前需要客户去调用这个函数。
相关文章:
C++学习笔记----9、发现继承的技巧(三)---- 尊重父类(1)
当写继承类的时候,需要清楚父类与子类之间的交互。像生成顺序,构造函数链,以及转化都可以是问题的根源。 1、父类构造函数 对象不会马上就能干活;它们必须由父类以及所包含的任意对象进行构建。c定义了如下的生成顺序:…...
启动service报错ORA-44317: database open read-only
ADG(RAC)备库环境,srvctl添加service服务成功,启动service时报错ORA-44317: database open read-only。 这是预期行为, 使用“srvctl add service -d <db_name> -s <service_name>”创建服务时,…...
GNU/Linux - Savannah项目
* 我们托管在自由操作系统上运行的自由项目,不依赖任何专有软件。 * 我们的服务使用 100% 的自由软件运行,包括服务本身。 * We host free projects that run on free operating systems and without any proprietary software dependencies. * Our se…...
Debug-028-el-carousel走马灯-当展示图片为2的问题处理
前言: el-carousel走马灯又是给elementui填坑的一天。el-carousel走马灯其实类似小程序中的轮播图。这里担心涉及版权问题就不贴项目中的图了。简单阐述一下问题:正常使用el-carousel时,如果图片数量大于等于3时,可以定时自动顺序…...
TapData 知识库 | 一文吃透数据整合(Data Consolidation)
顾名思义,数据整合指的是将不同来源的数据汇集在一起,并将其集中存储于一个统一的数据平台。数据整合使用户能够通过单一访问入口获取数据,进而推动数据洞察的生成与分析。 数据通常被简单地看作信息的集合,仿佛默认每个数据单元在…...
MySQL数据的导出
【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…...
微服务--OpenFeign【重点】
如果哪天 我们硬编码写的接口变了,只要写过该接口的 都要改,太麻烦了, 所以 就用 OpenFeign 来解决这个麻烦 了解: SimpleClientHttpRequestFactory和 HttpComponentsClientHttpRequestFactory 都是Spring框架中用于创建ClientH…...
【力扣打卡系列】滑动窗口与双指针(两数之和)
坚持按题型打卡&刷&梳理力扣算法题系列,语言为go,Day1 两数之和 题目描述 解题思路 采用哈希表 将nums[i] nums[j] target 转化成 nums[i] target - nums[j]去思考新建一个map来存储,键为值(左边的)&#…...
蚂蚁华东师范大学:从零开始学习定义和解决一般优化问题LLMOPT
🎯 推荐指数:🌟🌟🌟 📖 title:LLMOPT: Learning to Define and Solve General Optimization Problems from Scratch 🔥 code:https://github.com/caigaojiang/LLMOPT &am…...
价格游戏的终章:品牌如何在通货膨胀时代智取市场
来源:The era of price-led profit growth is coming to an end (marketingweek.com) 近年来,通货膨胀促使许多品牌通过提价来提升利润,而销量几乎没有受到太大影响。然而,随着通货膨胀放缓,继续提价的策略可能会吸引…...
CVTE Android面试题及参考答案
Activity 的生命周期 Activity 的生命周期分为以下几个主要状态: onCreate ():在 Activity 第一次被创建的时候调用。通常在这个方法中进行一些初始化操作,如设置布局、初始化成员变量等。这是 Activity 进入可见状态的第一步。onStart ():当 Activity 即将对用户可见的时候…...
Docker实战:从入门到进阶
Docker实战:从入门到进阶 引言 Docker是一个开源的应用容器引擎,它允许开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何支持Docker的平台上。本文将通过实战和应用举例,带领大家深入了解Docker的强大…...
Jupyter Notebook汉化(中文版)
原版jupyter notebook是英文的,想要将其改为中文 在jupyter notebook所在环境输入以下命令 pip install jupyterlab-language-pack-zh-CN打开jupyter notebook,在设置语言中将其设置为中文...
C#的小数位保留以及四舍五入
C#使用Math.Round("数值","保留位","保留方式")进行小数位保留以及四舍五入 //1.MidpointRounding.ToEven(四舍六入五成双) //当保留小数位后一位为0~4时,舍去末位 var x1 Math.Round(1.124, 2, MidpointRo…...
KNNImputer
KNNImputer实例是指在使用Python的scikit-learn库时,通过sklearn.impute.KNNImputer类创建的一个对象,该对象专门用于处理数据集中的缺失值。KNNImputer采用K-近邻(K-Nearest Neighbors,KNN)算法来估算并填充这些缺失值…...
RHCE例行性工作笔记
1、单一执行的例行性工作 单一执行的例行性工作: 仅处理执行一次就结束了 at命令的工作过程 /etc/at.allow ,写在该文件的人可以使用 at 命令 /etc/at.deny ,黑名单 两个文件如果都不存在,只有 root 能使用 #at 工作调度对应的…...
ros2 action server示例、拓展、练习
注意:以下代码全部由ai生成,没有大问题,运用时需根据报错逐步调试 action server示例 将 goal、result 和 feedback 作为类的成员变量的 C 示例代码: 示例代码 #include "rclcpp/rclcpp.hpp" #include "rclcpp…...
【Go语言】安装及使用基础教程
文章目录 1. 下载安装Go官网安装使用 Homebrew 安装 (Mac)创建工作目录 (Workspace)设置环境变量通过 VSCode 扩展商店安装 Go 插件处理权限问题 2. Hello, World 示例3. 语法基础变量声明常量数组切片(Slice)Map(集合)控制结构fo…...
【大模型】3分钟了解提示(Prompt)工程、检索增强(RAG)和微调
我们先看下面这个图: 简单理解大模型是通过海量训练数据训练出来的,它的能力非常强,但是有时候会给出错误的回答。那产生错误的原因可能是什么呢? 1.提问错误(提示工程) 在我们提问的方式不对的情况下&a…...
太速科技-509-基于XCVU13P的4路QSFP28光纤PCIeX16收发卡
基于XCVU13P的4路QSFP28光纤PCIeX16收发卡 一、板卡概述 基于XCVU13P的4路QSFP28光纤PCIeX16收发卡。该板卡要求符合PCIe 3.0标准,包含一片XCVU13P-2FLGA2014I、4组64-bit/8GB DDR4;4路QSFP28 4X光纤,每路光纤支持4X25Gbps&#…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
