【语法】C++继承中遇到的问题及解决方法

目录

1.子类构造函数中初始化父类成员

2.子类显式调用父类的析构函数

第一种说法:重定义

反驳:

第二种说法:operator~

3.因编译器版本过低而出现错误


贴主在学习C++的继承时,遇到了很多问题,觉得很变态,特此发帖分享

1.子类构造函数中初始化父类成员

众所周知,在子类的构造函数中是不能初始化父类成员的,必须调用父类的构造函数来完成初始化

#include <iostream>
using namespace std;
class base
{
public:base():_a(0){cout << "constructor base \n";}
protected:int _a;
};class derived : public base
{
public:derived():_a(0)//报错,因为_a是父类成员{cout << "constructor derived \n";}
};

 但有一种情况可能会被误认为是初始化

#include <iostream>
using namespace std;
class base
{
public:base():_a(0){cout << "constructor base \n";}
protected:int _a;
};class derived : public base
{
public:derived()//:base()这里已经隐式调用了base的构造函数了{_a = 0;cout << "constructor derived \n";}
};

这确实可以运行,但这里其实并不是初始化(initialization),而是赋值(assignment),这里看似没有调用父类的初始化,但其实会隐式调用构造函数

可以看到,base的构造函数还是被调用了,这就是隐式调用了base的构造函数 

那拷贝构造函数呢?若子类不定义默认构造函数的话,拷贝构造时确实会调用父类的拷贝构造函数完成父类函数的初始化

#include <iostream>
using namespace std;
class base
{
public:base():_a(0){cout << "constructor base \n";}base(const base& b):_a(b._a){cout << "copy constructor base \n";}
protected:int _a;
};class derived : public base
{
public:derived():base(){cout << "constructor derived \n";}
};int main()
{derived a;cout << "\n";derived b(a);return 0;
}

输出结果:

 可以看到在拷贝构造时,隐式调用了父类的拷贝构造

但如果子类有用户定义的拷贝构造函数,如果没有显式调用父类的拷贝构造函数,编译器就会隐式调用父类的构造函数

#include <iostream>
using namespace std;
class base
{
public:base():_a(0){cout << "constructor base \n";}base(const base& b):_a(b._a){cout << "copy constructor base \n";}
protected:int _a;
};class derived : public base
{
public:derived():base(){cout << "constructor derived \n";}derived(const derived& d)//子类拷贝构造不显式调用父类的拷贝构造,会隐式调用父类的构造函数完成父类成员初始化{cout << "copy constructor derived \n";}
};int main()
{derived a;cout << "\n";derived b(a);return 0;
}

输出结果:

可以看到,在拷贝构造时,编译器先隐式调用了父类的构造函数

2.子类显式调用父类的析构函数

众所周知,子类的析构函数会在被调用完成后自动调用父类的析构函数清理父类成员。因为这样才能保证子类对象先清理子类成员再清理父类成员的顺序。

#include <iostream>
using namespace std;
class base
{
public:base(){cout << "constructor base \n";}~base(){cout << "destructor base \n";}
};class derived : public base
{
public:derived(){cout << "constructor derived \n";}~derived(){~base();cout << "destructor derived \n";}//会报错
};int main()
{derived a;return 0;
}

报错: 

当然,对于99%的情况来说,根本用不到显式调用析构函数,剩下的1%就是placement new 管理内存 的情况

对于这里的报错,我目前知道两种说法

第一种说法:重定义

这里的~base和~derived构成重定义(隐藏),但他们两个函数名不相同啊?这是因为经过编译器处理后所有的析构函数都会被处理成destructor(为了支持多态),那他们两个析构函数就是重名函数了,父类的析构函数就会被重定义,此时要想让编译器知道你要调用的是父类的析构,就要在前面加上作用域限定符

int main()
{cout << int() <<endl;return 0;
}

反驳:

“隐藏”是非限定名查找的一部分。而析构函数的查找方式有所不同,因此不存在隐藏关系。也没有所谓的“转换为特殊名称 destructor(析构函数)”这样的过程作为名称查找的目的。 

如果有大佬知道到底对不对的,欢迎评论区解答!!!

第二种说法:operator~

这里的报错和是不是继承没有关系,因为下面代码也会报同样的错误

class base
{~base(){cout << "~base \n";}void func(){~base();}
};

这段代码的执行顺序其实是先base(),再~

base()会先构造一个base的临时对象。这里其实不难理解,就例如int(),会调用int的默认构造函数创建临时的int变量,它的值是0

int main()
{cout << int() <<endl;return 0;
}

输出:0

所以base()也一样,会调用base的默认构造函数来构建一个临时的base对象

然后会试图调用base对象的operator~重载,如下图

#include <iostream>
using namespace std;
class base
{
public:base(){cout << "constructor base \n";}~base(){cout << "destructor base \n";}void operator~(){cout << "operator ~ \n";}
};class derived : public base
{
public:derived(){cout << "constructor derived \n";}~derived(){base::~base();cout << "destructor derived \n";}
};
int main()
{~base();cout <<"\n";base().operator~();//第一行就相当于这么调用return 0;
}

输出结果:

如上图,第一个和第一行和第三行的代码输出结果相同

所以报错的原因就是base类没有operator~重载

流程:先构造一个临时的base对象,再调用临时base对象的operator~运算符,最后析构这个临时的base对象

3.因编译器版本过低而出现错误

 根据第二个问题可以得知,当写出~base()时,会先构造一个临时的base对象,然后调用该对象的operator~操作符,最后析构这个临时base对象

而如果要直接在子类中调用父类析构函数,可以这样

class derived : public base
{
public:derived(){cout << "constructor derived \n";}~derived(){base::~base();cout << "destructor derived \n";}
};

~base()的前面加上作用域限定符,这样编译器就知道你是要调用父类的析构函数,而不是operator~重载。

但如果在父类有operator~重载时,在子类中调用base::~base();呢?

#include <iostream>using namespace std;
class base
{
public:base(){cout << "constructor base \n";}~base(){cout << "destructor base \n";}void operator~(){cout << "operator ~ \n";}
};class derived : public base
{
public:derived(){cout << "constructor derived \n";}~derived(){base::~base();cout << "destructor derived \n";}
};
int main()
{derived d;return 0;
}

上面代码在我之前的编译器中,就会编译错误

error:

cmd /c chcp 65001>nul && C:\mingw64\bin\g++.exe -fdiagnostics-color=always -g "D:\Valkyrie-text\simple text\text.cpp" -o "D:\Valkyrie-text\simple text\text.exe"
D:\Valkyrie-text\simple text\text.cpp: In destructor 'derived::~derived()':
D:\Valkyrie-text\simple text\text.cpp:15:28: error: no matching function for call to 'derived::~derived()'~derived(){base::~base();cout << "destructor derived \n";}^
D:\Valkyrie-text\simple text\text.cpp:7:5: note: candidate: 'base::~base()'~base(){cout << "destructor base \n";}^
D:\Valkyrie-text\simple text\text.cpp:7:5: note:   candidate expects 1 argument, 0 provided

后来发现,这是编译器在抱怨缺少参数

在前面加上thts->就可以了

this->base::~base();

相信又很多人运行上面代码时都会报和我一样的错误,这是因为编译器的版本太低

我之前的编译器是8.1.0的gcc

现在更新了,用14.2.0的版本就不会再报这个错了,所以强烈建议大家更新一下自己的编译器!

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

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

相关文章

前缀和 后缀和 --- 寻找数组的中心下标

题目链接 寻找数组的中心下标 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为…

NVIDIA --- 端到端自动驾驶

前言 参加了NVIDIA 高级辅助驾驶开发者实验室的活动&#xff0c;本次活动基于 NVIDIA 汽车行业的端到端解决方案——DRIVE AGX™ 平台&#xff0c;实现高级别智能和安全性的软硬件开发工具和 AV 基础设施。并且NVIDIA自动驾驶实验室推出了一系列自动驾驶算法最新的前沿研究视频…

SQL实战:03之SQL中的递归查询

文章目录 概述SQL 中的递归实现题目一:分析组织层级题解题目二:树节点求解题解步骤一&#xff1a;通过递归查询出每个节点的上级节点和下级节点分布步骤二&#xff1a;分组统计 概述 最近刷题时遇到了一道需要根据组织层级来统计各个层级的一些数据&#xff0c;当时碰到时的第…

MySQL 语法与基础完全指南

MySQL 是最流行的开源关系型数据库管理系统之一&#xff0c;广泛应用于 Web 应用程序开发。本文将全面介绍 MySQL 的基础知识和完整语法结构。 一、MySQL 基础概念 1. 数据库基本术语 数据库(Database): 存储数据的集合 表(Table): 数据以表格形式组织 列(Column): 表中的一…

【Sqlalchemy Model转换成Pydantic Model示例】

【Sqlalchemy Model转换成Pydantic Model示例】 由于Sqlalchemy和Pydantic的模型字段类型可能有差异, 所以需要一个通用的装换类 def sqlalchemy_to_pydantic_v2(sqlalchemy_model, pydantic_model):"""通用函数&#xff0c;将 SQLAlchemy 模型实例转换为 Pyd…

2025年欧洲西南部大停电

2025年4月28日&#xff0c;欧洲西南部出现大规模停电&#xff0c;西班牙、葡萄牙和法国南部均受到影响。有报道指出停电可能与 欧洲电网出现问题有关&#xff0c;但最终原因尚未确定。由于停电&#xff0c;上述地区的交通和通信服务均受到严重影响&#xff0c;交通信号灯停止工…

Java EE初阶——计算机是如何工作的

1. cpu 冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09; • CPU 中央处理器: 进⾏算术运算和逻辑判断. • 存储器: 分为外存和内存, ⽤于存储数据(使⽤⼆进制⽅式存储) • 输⼊设备: ⽤⼾给计算机发号施令的设备. • 输出设备: 计算机个⽤⼾汇报结果的设…

飞鸟游戏模拟器 1.0.3 | 完全免费无广告,内置大量经典童年游戏,重温美好回忆

飞鸟游戏模拟器是一款专为安卓用户设计的免费游戏模拟器&#xff0c;内置了大量经典的童年游戏。该模拟器拥有丰富的游戏资源&#xff0c;目前已有约20,000款游戏&#xff0c;包括多种类型如冒险、动作、角色扮演等。用户可以直接搜索查找想要玩的游戏进行下载并启动。游戏库中…

网络爬取需谨慎:警惕迷宫陷阱

一、技术背景:网络爬虫与数据保护的博弈升级 1. 问题根源:AI训练数据爬取的无序性 数据需求爆炸:GPT-4、Gemini等大模型依赖数万亿网页数据训练,但大量爬虫无视网站的robots.txt协议(非法律强制),未经许可抓取内容(如新闻、学术论文、代码),引发版权争议(如OpenAI被…

Qwen3简介:大型语言模型的革命

Qwen3简介&#xff1a;大型语言模型的革命 Qwen系列语言模型的最新发布——Qwen3&#xff0c;标志着人工智能&#xff08;AI&#xff09;技术的一次重大飞跃。基于前代版本的成功&#xff0c;Qwen3在架构、推理能力和多项先进功能上都取得了显著提升&#xff0c;正在重新定义大…

MODSIM选型指南:汽车与航空航天企业如何选择仿真平台

1. 引言 在竞争激烈的汽车与航空航天领域&#xff0c;仿真技术已成为产品研发不可或缺的环节。通过在设计阶段验证概念并优化性能&#xff0c;仿真平台能有效缩短开发周期并降低物理样机制作成本。 MODSIM&#xff08;建模与仿真&#xff09;作为达索系统3DEXPERIENCE平台的核…

linux 内核 debugfs 使用介绍

一&#xff1a;概述 debugfs 是 Linux 内核提供的一个特殊的虚拟文件系统&#xff0c;用于 暴露内核模块&#xff08;如驱动&#xff09;内部的调试信息或控制接口&#xff0c;供开发者、调试人员实时查看和排查问题。即 debugfs 就是一个“调试专用的 /proc 或 /sys”&#xf…

ZYNQ笔记(十五):PL读写PS DDR(自定义IP核-AXI4接口)

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;PL 端自定义一个 AXI4 接口的 IP 核&#xff0c;通过 AXI_HP 接口对 PS 端 DDR3 进行读写 测试&#xff0c;读写的内存大小是 4K 字节&#xff0c; 目录 一、介绍 &#xff08;1&#xff09;…

Redis 小记

Redis 命令小记 Redis 是一个文本/二进制数据库&#xff08;textual/binary database&#xff09; CLI 命令 redis-cli, redis-server, redis-benchmark, redis-check-dump, redis-check-aof redis-cli 执行命令 # 方式 1 redis-cli -h 127.0.0.1 -p 6379 > 127.0.0.1:63…

如何在idea中编写spark程序

在 IntelliJ IDEA 中编写 Spark 程序的详细指南 在大数据处理领域&#xff0c;Apache Spark 凭借其强大的分布式计算能力&#xff0c;成为了众多开发者的首选工具。而 IntelliJ IDEA 作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;为编写 Spark 程序…

各类神经网络学习:(十一)注意力机制(第3/4集),位置编码

上一篇下一篇注意力机制&#xff08;2/4集&#xff09;注意力机制&#xff08;4/4集&#xff09; 位置编码 R N N RNN RNN 和 L S T M LSTM LSTM 这些网络都是串行执行的&#xff0c;在潜移默化中&#xff0c;就包含了顺序关系&#xff0c;也就是词序关系。而注意力机制是并行…

《Python Web部署应知应会》Flask网站隐藏或改变浏览器URL:从Nginx反向代理到URL重写技术

Flask网站隐藏或改变浏览器显示URL地址的实现方案&#xff1a;从Nginx反向代理到URL重写技术 引言 在Web应用开发中&#xff0c;URL路径的安全性往往被忽视&#xff0c;这可能导致网站结构和后端逻辑被攻击者轻易推断。对于Flask框架开发的网站&#xff0c;如何隐藏或改变浏览…

elementui里的el-tabs的内置样式修改失效?

1.问题图 红框里的是组件的内置样式&#xff0c;红框下的是自定义样式 2.分析 2.1scoped vue模板编译器在编译有scoped的stye标签时&#xff0c;会生成对应的postCSS插件&#xff0c;该插件会给每个scoped标记的style标签模块&#xff0c;生成唯一一个对应的 data-v-xxxhash…

大数据测试集群环境部署

Hadoop大数据集群搭建&#xff08;超详细&#xff09;_hadoop_小飞飞519-GitCode 开源社区 hadoop集群一之虚拟机安装(mac)_hadoop_皮皮虾不皮呀-华为开发者空间 hadoop集群二之hadoop安装_hadoop_皮皮虾不皮呀-华为开发者空间 虚拟机如何查看gateway | PingCode智库

Nginx 核心功能笔记

目录 一、Nginx 简介 二、核心功能详解 三、关键指令解析 四、性能优化要点 五、常见应用场景 一、Nginx 简介 定位 高性能的 HTTP/反向代理服务器&#xff0c;同时支持邮件协议代理&#xff08;IMAP/POP3/SMTP&#xff09;。采用 事件驱动、异步非阻塞 架构&#xff0c;…