《EffectiveC++》读书笔记(2):构造/析构/赋值运算

C语言与CPP编程 2023-06-16 15:51

击上方“C语言与CPP编程”,选择“关注/置顶/星标公众号

干货福利,第一时间送达!最近有小伙伴说没有收到当天的文章推送,这是因为微信改了推送机制,确实会一部分有小伙伴刷不到当天的文章,一些比较实用的知识和信息,错过了就是错过了。所以建议大家加个星标⭐️,就能第一时间收到推送了。


小伙伴们大家好,我是飞宇

今天继续更新《Effective C++》和《C++并发编程实战的读书笔记,下面是已经更新过的内容:

《C++并发编程实战》读书笔记(1):并发、线程管控

《C++并发编程实战》读书笔记(2):并发操作的同步

《Effective C++》读书笔记(1):让自己习惯C++


条款5、了解C++默认编写并调用哪些函数    

    通常情况下,如果代码中没有声明构造函数、拷贝构造函数、拷贝运算符、析构函数,编译器会在需要时创建他们,但这往往只能满足编译器的需求,很可能无法满足程序的需求。

    实际的生成规则复杂一些,可以查阅cppreference。

class Empty{};//上面的空类好比下面的类class Empty{public:  Empty() { ... }  Empty(const Empty& rhs) { ... }  ~Empty() { ... }    Empty& operator=(const Empty& rhs) { ... }};

    编译器生成的构造函数/析构函数是只是调用基类和非静态成员变量的构造函数/析构函数;生成的析构函数是非虚的,除非基类有虚析构函数。

    至于生成的拷贝构造函数和拷贝操作符只是单纯将每个非静态成员变量拷贝;有const成员或者引用成员时,以及基类拒绝拷贝操作符时,默认生成的拷贝操作符没有意义,必须自己定义。  



条款6、若不想使用编译器自动生成的函数,就该明确拒绝

    很多时候,你并不希望某些类被拷贝,而仅仅不实现拷贝构造/拷贝运算符是不够的,因为编译器可能会自行生成。

    为此,可以把拷贝构造/拷贝运算符声明为"=delete",或者声明为private(后者较为过时)。

class Uncopyable{public:  Uncopyable() = delete;  Uncopyable& operator=(const Uncopyable&) = delete;};

    事实上,对于析构函数中需要释放资源的类,为了防止内存问题,除非真的需要拷贝功能,否则最好都禁止拷贝。

    可以让它们继承上面的类,即可禁止编译器生成拷贝操作:编译器试图为它们生成拷贝构造/拷贝运算符时会尝试调用基类Uncopyable的对应操作,而这会被拒绝。

class SomeClass: private Uncopyable{ ... };


条款7、为多态基类声明virtual析构函数

    C++中多态性质体现于虚函数:基类指针或引用调用虚函数时会检查指向的对象是基类还是派生类,再调用对应的函数。其具体实现这里不再赘述。

    当这样的一个指向派生类的基类指针析构时,如果析构函数不是虚函数,则直接调用基类的析构函数,那么派生类获取的资源未释放,则会造成内存泄漏。

    而当析构函数是虚函数时则先调用对应的派生类析构函数,再调用基类析构函数,资源全部释放。

    不过这种操作只有在基类是多态用途时才需要注意,也有很多类不是为了多态的用途,例如STL容器和上文的Uncopyable。



条款8、别让异常逃离析构函数

    C++中抛出异常时会逐步展开其函数调用栈,清空局部资源,直到异常被catch。

    如果析构函数可以抛出异常,那么清空局部资源时局部对象的析构函数再次异常时同时存在两个异常,C++无法处理,可能会过早结束或出现不明确行为。因此,析构函数绝对不要抛出异常,应通过try-catch捕获任何异常。

    有时,客户需要处理某些异常,那么类应该提供一个普通成员函数执行相关操作,供用户调用并处理异常。

    例如数据库连接这样的类中,假设用户需要处理关闭连接时的异常,同时析构函数不能抛出异常,可以这样:

class DBConn{public:  void close(){    db.close();    closed = true;  }  ~DBConn(){    if(!closed){      try{        db.close();      }catch(...){        //记录相关信息      }    }  }private:  DBConnection db;  bool closed;};

条款9、绝不在构造和析构过程中调用virtual函数

    C++的构造过程是先构造基类再构造子类、先初始化再进入构造函数体;析构过程相反。

    对于派生类的构造函数而言,进入其中时基类部分已构造完而派生类部分未构造完,对象类型是基类,故而此时调用虚函数,实际上使用的是基类的虚函数。

    析构函数同理。进入析构函数后派生类部分呈未定义值,对象类型是基类,调用的是基类的虚函数。

    总而言之,在构造函数与析构函数中虚函数的行为有特殊变化;为了避免出错,不要在其过程中使用虚函数。如果真的有需求,可以改造成非虚函数再使用。

条款10、令operator=返回一个reference to *this

    C++中通常支持连锁赋值,采用右结合律:

int a,b,c;a = b = c = 15;// a = (b = (c = 15));

    为了支持这个常规,拷贝运算符需要返回一个引用。这一条款并不强制,但约定俗成。

class Widget{public:  Widget& operator=(const Widget& rhs){    ...    return *this;  }};

条款11、在operator=中处理“自我赋值”

    考虑这样一个类,其中管理了一个堆对象

class Widget{  ...private:  Bitmap* pb; //一个堆对象};

    总有些时候,会出现实质上“a=a”这种自我赋值的情况。那么这样简单的拷贝运算符就会出错,先释放了自身的pb,又使用了pb:

Widget& Widget::operator=(const Widget& rhs){  delete pb;  pb = new Bitmap(*rhs.pb);  return *this;}

    传统做法是函数开头添加一个测试:

if(this == &rhs) return *this;

    这种做法具备“自我赋值安全性”,但不具备“异常安全性”:例如当new Bitmap异常时,pb指向被删除的内存。

    常用的方法有两种,兼顾了“自我赋值安全性”与“异常安全性”:

//第一种Widget& Widget::operator=(const Widget& rhs){  Bitmap* pb_original = pb;  pb = new Bitmap(*rhs.pb);  delete pb_original;  return *this;}//第二种void Widget::swap(Widget& rhs){  ...};Widget& Widget::operator=(const Widget& rhs){  Widget temp(rhs);  swap(temp);  return *this;}

条款12、复制对象时勿忘其每一个成分

    当自己实现拷贝构造/拷贝运算符时,编译器不会警告你遗漏了某些成员变量。因此,必须仔细地复制所有派生类成员,并调用基类的拷贝操作来复制基类成员。

Derived::Derived(const Derived& rhs)  :Base(rhs),some_member(rhs.some_member){}
Derived& Derived::operator=(const Derived& rhs){  Base::operator=(rhs);  some_member = rhs.some_member;  return *this;}

    拷贝构造和拷贝运算符中很可能有相当多重复的操作,但因为两个函数性质完全不同,因此不能用其中一个调用另一个来减少冗余。

    可以把共同功能放在第三个函数中,并由两个拷贝操作共同调用。

EOF

你好,我是飞宇,本硕均于某中流985 CS就读,先后于百度搜索以及字节跳动电商等部门担任Linux C/C++后端研发工程师。

同时,我也是知乎博主@韩飞宇,日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。

有个朋友收集了一些C++开发手册、LeetCode刷题模板等精品资料,可加他的微信免费领取。

C语言与CPP编程 C语言/C++开发,C语言/C++基础知识,C语言/C++学习路线,C语言/C++进阶,数据结构;算法;python;计算机基础等
评论
  • 应用环境与极具挑战性的测试需求在服务器制造领域里,系统整合测试(System Integration Test;SIT)是确保产品质量和性能的关键步骤。随着服务器系统的复杂性不断提升,包括:多种硬件组件、操作系统、虚拟化平台以及各种应用程序和服务的整合,服务器制造商面临着更有挑战性的测试需求。这些挑战主要体现在以下五个方面:1. 硬件和软件的高度整合:现代服务器通常包括多个处理器、内存模块、储存设备和网络接口。这些硬件组件必须与操作系统及应用软件无缝整合。SIT测试可以帮助制造商确保这些不同组件
    百佳泰测试实验室 2024-12-12 17:45 106浏览
  • 在智能化技术快速发展当下,图像数据的采集与处理逐渐成为自动驾驶、工业等领域的一项关键技术。高质量的图像数据采集与算法集成测试都是确保系统性能和可靠性的关键。随着技术的不断进步,对于图像数据的采集、处理和分析的需求日益增长,这不仅要求我们拥有高性能的相机硬件,还要求我们能够高效地集成和测试各种算法。我们探索了一种多源相机数据采集与算法集成测试方案,能够满足不同应用场景下对图像采集和算法测试的多样化需求,确保数据的准确性和算法的有效性。一、相机组成相机一般由镜头(Lens),图像传感器(Image
    康谋 2024-12-12 09:45 113浏览
  • 本文介绍瑞芯微RK3588主板/开发板Android12系统下,APK签名文件生成方法。触觉智能EVB3588开发板演示,搭载了瑞芯微RK3588芯片,该开发板是核心板加底板设计,音视频接口、通信接口等各类接口一应俱全,可帮助企业提高产品开发效率,缩短上市时间,降低成本和设计风险。工具准备下载Keytool-ImportKeyPair工具在源码:build/target/product/security/系统初始签名文件目录中,将以下三个文件拷贝出来:platform.pem;platform.
    Industio_触觉智能 2024-12-12 10:27 104浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 129浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-12 10:13 70浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 76浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 109浏览
  • 首先在gitee上打个广告:ad5d2f3b647444a88b6f7f9555fd681f.mp4 · 丙丁先生/香河英茂工作室中国 - Gitee.com丙丁先生 (mr-bingding) - Gitee.com2024年对我来说是充满挑战和机遇的一年。在这一年里,我不仅进行了多个开发板的测评,还尝试了多种不同的项目和技术。今天,我想分享一下这一年的故事,希望能给大家带来一些启发和乐趣。 年初的时候,我开始对各种开发板进行测评。从STM32WBA55CG到瑞萨、平头哥和平海的开发板,我都
    丙丁先生 2024-12-11 20:14 92浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 128浏览
  • 铁氧体芯片是一种基于铁氧体磁性材料制成的芯片,在通信、传感器、储能等领域有着广泛的应用。铁氧体磁性材料能够通过外加磁场调控其导电性质和反射性质,因此在信号处理和传感器技术方面有着独特的优势。以下是对半导体划片机在铁氧体划切领域应用的详细阐述: 一、半导体划片机的工作原理与特点半导体划片机是一种使用刀片或通过激光等方式高精度切割被加工物的装置,是半导体后道封测中晶圆切割和WLP切割环节的关键设备。它结合了水气电、空气静压高速主轴、精密机械传动、传感器及自动化控制等先进技术,具有高精度、高
    博捷芯划片机 2024-12-12 09:16 101浏览
  • 全球智能电视时代来临这年头若是消费者想随意地从各个通路中选购电视时,不难发现目前市场上的产品都已是具有智能联网功能的智能电视了,可以宣告智能电视的普及时代已到临!Google从2021年开始大力推广Google TV(即原Android TV的升级版),其他各大品牌商也都跟进推出搭载Google TV操作系统的机种,除了Google TV外,LG、Samsung、Panasonic等大厂牌也开发出自家的智能电视平台,可以看出各家业者都一致地看好这块大饼。智能电视的Wi-Fi连线怎么消失了?智能电
    百佳泰测试实验室 2024-12-12 17:33 107浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-11 17:58 105浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦