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#从零开始学习(基本语法概念)(2)
深入C# 本章所有的代码都放在 https://github.com/hikinazimi/head-first-Csharp 控制台项目结构 每个C#程序采用同样的方式组织,命名空间,类和方法 using System;namespace helloworld//命名空间 {class Program//类{static void Main(string[] args)//程序入口{Console.Writ…...

基于SSM+微信小程序的家庭记账本管理系统(家庭1)
👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 1、管理员端功能有首页、个人中心、用户管理,消费详情管理、收入详情管理、系统管理等。 2、用户端功能有首页、消费详情、收入详情、论坛信息、我的等功能。 2、项目技术 …...

MEMC功能详解
文章目录 MEMC的工作原理:优点:缺点:适用场景:1. Deblur(去模糊)2. Dejudder(去抖动)总结两者区别: MEMC(Motion Estimation and Motion Compensation&#x…...

C++ | Leetcode C++题解之第493题翻转对
题目: 题解: class BIT { private:vector<int> tree;int n;public:BIT(int _n) : n(_n), tree(_n 1) {}static constexpr int lowbit(int x) {return x & (-x);}void update(int x, int d) {while (x < n) {tree[x] d;x lowbit(x);}}in…...

Git 修改分支名
在Git中修改分支名称,可以使用以下步骤: 切换到要重命名分支之外的其他分支: git checkout <其他分支名>重命名本地分支: git branch -m <旧分支名> <新分支名>如果需要删除远程的旧分支并创建新分支࿱…...

[自动化测试:Selenium]:环境部署和Webdriver的使用
文章目录 修改安装源打开Python Packages。点击梅花按钮。在弹出的对话框中,填入Name(随便填),Repository URL,选择下列的源,一般先选择清华源按OK确认。配置完成 安装seleniumFile→Settings→Project&…...

51单片机——OLED显示图片
取模软件:链接:https://pan.baidu.com/s/1UcrbS7nU4bsawNxsaaULfQ 提取码:gclc 1、如果图片大小和格式不合适,可以先用Img2Lcd软件进行调整图片大小,一般取模软件使用的是.bmp图片,可以进行输出.bmp格式。软件界面如下࿱…...

Gin 协程mysql客户端
一、Gin框架 mysql配置 这里选择yaml文件配置 二、配置读取 viper 读取yaml文件中对应配置 三、mysql 的协程客户端 文件位置 package databaseimport ("database/sql""fmt""github.com/spf13/viper""log""net/http"&quo…...

量子门电路开销——T门、clifford门、toffoli门、fredkin门
在量子计算中,T门的成本比Clifford门高出很多倍的原因与量子计算中纠错的实现、物理门操作的复杂性以及容错量子计算架构中的成本评估有关。以下是几个关键原因,解释了为什么 T 门的成本在量子计算中远远高于 Clifford 门: 1. T 门和 Cliffo…...

C++之《剑指offer》学习记录(1):类型转换关键字
笔者最近在找工作时,无意间读到了一本名为《剑指offer》的书,粗略翻阅了一下,感觉这将会是一本能让我不再苦恼于笔试和面试“手搓代码”的书。故笔者写下该系列博客记录自己的学习历程,希望能和这本书的读者朋友们一起交流学习心得…...