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

C++:string模拟实现中的赋值拷贝函数现代写法诡异地崩掉了......

事情是这样的:

博主今天回看以前实现过的string,当时就遇到了一个bug:

可见博主当时的破防。因为最近在集中复盘C++初阶部分,就有点好奇年轻的时候自己写的模拟string是什么样。没想到给我自己留了个bug。


现在来细看这个场景:为了测试自己写的赋值拷贝现代写法效果,有了这个函数。

	void test11(){//现代构造的写法string s("helloworld");string s2(s);// 现代拷贝构造cout << s2 << endl;string s3("cherry magic");s2 = s3;// 现代赋值拷贝cout << s2 << endl;}

当来到165行时——即我打算让s3赋值给现有的对象s2,此时自然要调用我亲手写的赋值拷贝

图1 调试到165行
图2 亲手写的赋值拷贝

当我按F11,企图跳进operator=里时,却是来到了拷贝构造这里。

嗯?此举何意啊——正常意料:我亲手写的赋值拷贝operator=传的参数是传值传参(传值(自定义类型string)传参都要雷打不动先拷贝构造一份)

图2 继续下一步,来到了string的拷贝构造函数

没错,拷贝构造我也写出了现代写法——避免这里长篇大论,现代写法我几句话说个大概,说不定读者看完就能上手写。

string tmp(s.c_str());//tmp建立在当前函数栈上,等当前函数结束,函数栈帧被销毁,生命周期也结束了。

当这句话执行完,tmp就是存在于当前栈上,但内容是拷贝s的string对象。我们拷贝构造函数的目的,就是让当前的*this(未构造的对象)拷贝传入对象s的内容,完成对象初始化。

无论如何,当拷贝构造函数结束,*this是完全实例化,tmp却要走向独属它的落幕(因为tmp是string自定义类型对象,它自动会调用析构完成资源释放)。

tmp拷贝完了,*this还没拷贝。怎么办?把*this有个大胆的想法——

swap(tmp);// 完整写 this->swap(tmp); 或者 (*this).swap(tmp);

看我的批注:*this用自己的成员函数将tmp和自己交换——交换了拷贝的内容。*this就这么轻轻一换完成了拷贝构造,tmp就拿着本来无用的信息去析构了。


我们继续按F11,等此次拷贝构造结束——跳入operator=的参数就构造完成,我们就进入operator=看看问题出在哪。

图3:先拷贝构造
图4:string tmp(s.c_str())已然完成
图5:*this拷贝构造完成

tmp此时该走向独属于它的落幕——身为string家中的长(栈)子,它有自己的使命(*this是次子(bushi

图6:tmp开始析构
图7:首先析构的——tmp的成员变量_str指向堆上的资源
图8:报错了,牛

其实刚刚那一步,可能有读者看出不对劲了。tmp交换过来的内容,应该是完全没被初始化的——怎么可能会有需要的释放的资源?那个_str也是生得奇怪——恰好是随机值,躲过了判空检查,所以避无可避地执行了delete[] _str;

我今天在改这个代码的时候,其实没注意到这点:因为出了构造函数完成了参数的拷贝构造,我想着此时该进入operator=了。直接按下了F10——跳过了tmp的析构,直接出现了这一幕。

所以下意识判断是operaor=的问题:这也是为什么我会当时写下这句判断——

图9:看得出来年轻时候第一次写现代写法,很没底气。一报错就怀疑上了

真相是什么?

今天在改代码的时候,我发现因为*this天生生得潦草,_str这个指针怎么生都是随机值——所以tmp换到它的内容,必走析构里的delete[] _str;——这件事让我很是苦恼,知道哪里出问题了,却是下不了手。

有没有什么方法?有的,只是我忘了(我忏悔)

初始化列表

我今天也是开眼了,拷贝构造也是构造。只要是构造函数,类型的成员变量就会走初始化列表——初始化列表存在的意义就是对成员变量定义。

身为变量,有了声明自然要有定义。所以不管显示写初始化列表与否,成员变量都会被定义(即成员变量都会走初始化列表)。初始化列表是很客观的存在,不是我们不写就不存在的。

如果显示写初始化列表,相应成员变量都会被规定的值初始化;
如果不显示写,可以在声明的位置给出缺省值——没错,这里的缺省值就是给未显示写在初始化列表的成员进行定义

  • 如果声明都不给缺省值了,那编译器表示:“那我随意了”——对于内置类型,编译器会给它随机值(取决于编译器,行为不确定);对于自定义类型,会去调用它的默认构造(如果没有默认构造,会编译报错)

想起来了,我再看我的string成员变量声明——果然没有声明值,而且刚刚的拷贝构造也没显示初始化列表(完美地避开正确初始化机会)

图10:string类的成员变量声明
图11:加上缺省值,静态成员变量在其他地方定义了

再看效果:

图12:成功运行

我发现AI摘要比我写得完整,但我想没我写得有趣:(对只是我想)
摘要:博主复盘自己早期实现的string类时,发现了一个隐藏bug。问题出现在拷贝构造函数的现代写法中:当交换临时对象tmp和未初始化的*this时,由于未显式初始化_str成员变量,导致析构时delete了一个随机地址。通过给成员变量添加缺省值,解决了这个因未初始化引发的未定义行为。文章详细记录了调试过程,揭示了C++构造函数初始化列表的重要性,以及显示初始化成员变量的必要性。最终修复方案是为string类的成员变量添加了缺省值声明,使程序得以正确运行。


文章转载自:

http://zWKkObUh.dxqfh.cn
http://rlNURa2X.dxqfh.cn
http://nRL4C9A9.dxqfh.cn
http://EFqm8CIZ.dxqfh.cn
http://4UCqJj9s.dxqfh.cn
http://1zbuvlzk.dxqfh.cn
http://Pvhhd2tf.dxqfh.cn
http://SJ64X7sS.dxqfh.cn
http://8yFZ2789.dxqfh.cn
http://mnwisxMV.dxqfh.cn
http://OLyxxIJn.dxqfh.cn
http://kSK6Ronu.dxqfh.cn
http://flyJhhhd.dxqfh.cn
http://SEzBuwIj.dxqfh.cn
http://zNHTHfMD.dxqfh.cn
http://D2FNbGHw.dxqfh.cn
http://vEAV0oG2.dxqfh.cn
http://V0FinFIF.dxqfh.cn
http://GvsyUAvY.dxqfh.cn
http://m2rd5iMp.dxqfh.cn
http://9ZwJAVDU.dxqfh.cn
http://A4CybtIR.dxqfh.cn
http://DPSm2YzE.dxqfh.cn
http://VrawDC9l.dxqfh.cn
http://DDWF0rMl.dxqfh.cn
http://Wd2EUMwB.dxqfh.cn
http://cw5ZS4Fo.dxqfh.cn
http://bUuWOZ2c.dxqfh.cn
http://OKpjmeMv.dxqfh.cn
http://lLnvBF4R.dxqfh.cn
http://www.dtcms.com/a/385262.html

相关文章:

  • 构建AI大模型对话系统
  • Linux基本指令(9)
  • 64_基于深度学习的蝴蝶种类检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 3-12〔OSCP ◈ 研记〕❘ WEB应用攻击▸利用XSS提权
  • 3dma渲染噪点成因排查及优化方案
  • Lombok
  • React Scheduler(调度器)
  • 多任务数据集的具体使用场景
  • KITTI数据集
  • 什么云服务器更好用推荐一下!?
  • 根据Linux内核原理 LRU链表如何知道page的活动频繁程度?
  • 2025全球LoRaWAN模组技术对比与应用方案解析
  • 社区主题征文——「异构融合与存算一体:架构探索与创新实践」算力技术征文
  • Jenkins参数化构建
  • SIPJS对接FreeSWITCH强制媒体流走coturn relay地址
  • docker registry 私服搭建教程
  • 清除gradle缓存的某个依赖
  • MCP引导Agent示例
  • 【HTTP 响应状态码】从零到实战
  • 航线系统对频模块技术要点
  • 二十、DevOps落地:Jenkins基础入门(一)
  • 计网1.1
  • DDD领域驱动设计
  • 传智播客--MySQL
  • 不同 HLA II 类等位基因(HLA-DRB1*15:02)与 cGVHD 的关联差异
  • 【AI 辅助工作工具集合】持续更新
  • 核心信息防爬虫盗取技术方案
  • Linux网络:序列化和反序列化
  • Java 代理模式-JDK动态代理
  • RabbitMQ 消息持久化与可靠性