《Effective C++》重点摘要(二)

《Effective C++》第二章:构造/析构/赋值运算

  1. C++默认编写的函数。C++编译器如果没有发现以下函数,就会为类生成一份默认版本的:

    1) default构造函数

    2) default析构函数

    3) copy构造函数

    4) copy assignment操作符(=运算符)

    前两个函数并不总是产生,它只在编译器需要的时候才产生出来。后两个函数只保证以bitwise语义拷贝non-static成员,所以如果有指针、引用等non-static成员的类要么拒绝编译器产生的版本,要么自行小心实现这两个函数。

  2. 若不想使用编译器自动生成的函数,就明确拒绝之。关键在于需要明确正在编写的类是否允许这些默认函数的存在,如果确定不允许,那么,在C++98中可以将其声明为private访问权限的,而C++11中还可以将其声明为deleted。最好的做法是做一个通用的拒绝了编译器生成默认函数的基类,让不需要默认函数的类继承自它。
  3. 为多态基类声明virtual析构函数。如果基类的析构函数不是virtual的,那么基类的virtual table中不会有实际需要调用的析构函数,那么删除一个指向derived类指针的基类指针时,结果自然是不能确定的(通常是析构不完全)。
  4. 别让异常逃离析构函数(如归还资源)。如果析构函数中抛出了异常而没有被处理,那么析构函数该做的事情就很可能没做完,即便这个被它传播出来的异常被上层函数捕获并处理也无法为为抛出异常的对象做完它该做的事情。所以如果虚构函数中出现异常,要么abort程序,要么自行处理异常(析构函数吞下异常)。abort掉程序往往让人“心中一跳“,所以尽管“吞下异常”并不完美,也该如此。具体做法是为可能抛出异常的操作编写一个函数,并把这个函数暴露给客户,给客户一个自己处理异常的机会。如果客户没有自己处理,那么就在析构函数里替用户做这件事情。
  5. 绝不要在构造和析构过程中调用virtual函数。构造函数总是先调用基类的构造函数,所以在构造函数里调用虚函数在语义上总是不合理的,因为此时的虚函数一点都不“虚”,调用的是父类版本,析构函数情况也是一样,尽管总是后调用基类的析构函数,不,应该说,正是因为这样的调用顺序,使得虚函数“不虚”。
  6. 令operator=返回一个reference to *this。这样做,可以让对象能够做到连续赋值,让你设计的类更像一个内置type。这样返回一个引用是可行,因为*this不是一个局部变量,当离开operator=函数作用域时,*this不会被析构。
  7. 在operator=中处理“自我赋值”。编写operator=有四个基本要求:返回reference to *this,以const reference作为参数传递方式,确保深浅拷贝语义合适、处理自我赋值,即处理客户让一个对象=本身的操作,在编译器眼里这是合法的表达式,但是运行时有可能出错,解决之道是在copy之间先判断出入参数(是个引用)所指对象的地址和this是否相等。还有一个更高的要求是保证“异常安全”,这通常可以通过copy-and-swap实现。
  8. 复制对象时,勿忘其每一部分。这个有什么好说的,如果没有做到,就不是复制!!注意拷贝成员变量时需要的拷贝语义(深、浅拷贝),也不要忘了所有基类里的成员。也不要让拷贝构造函数和copy assignment函数相互调用,写一个共用的copy函数,让拷贝构造函数和copy assignment函数调用它。
时间: 2024-08-01 05:05:06

《Effective C++》重点摘要(二)的相关文章

阅读《LEARNING HARD C#学习笔记》知识点总结与摘要二

今天继续分享我的阅读<LEARNING HARD C#学习笔记>知识点总结与摘要二,仍然是基础知识,但可温故而知新. 七.面向对象 三大基本特性: 封装:把客观事物封装成类,并隐藏类的内部实现细节,仅开放相关的访问属性.方法等 继承:通过继承可以复用父类的代码: 多态:1.允许将子对象赋值给父对象,2.同方法在不同的对象上有不同的实现方式: 子类初始化顺序:初始化类的实例字段à调用父类构造函数à调用子类构造函数: 重写:父类声明为virtual或abstract的类成员(属性.方法),在子类继

BDD in Action 重点摘要

BDD in Action 重点摘要: BDD in Action ?对于需灰度发布的产品, 开发人员可经由BDD, 便可自保证需灰度发布产品的质量? ?所以, BDD 可使产品更快速的达到灰度发布的要求, 而可更快速的获得使用者对产品的反馈? ?在这样的开发模式下, 测试人员可更专注于探索性测试, 挖掘出更深层的产品缺陷, 回归到真正测试人员的专业?

《Effective C++》重点摘要(五)

<Effective C++>第五章:实现 尽可能延后变量定义式的出现时间.只有变量在恰好要使用之前定义,程序的可读性往往会得到提高,因为这样不容易忘记变量说代表的意思.另一方面,这样做可以提高程序性能,如果不需要一个变量时却要为它分配.释放空间,调用构造.析构函数,获取.释放资源--这,真是太浪费了.补充一点,声明式并不会做这些事情,所以可以考虑用声明式替换定义式以尽量延后变量的定义. 尽量少做转型动作.转型意味着出错的可能性大大提升,转型意味着更多的操作.如果不得不转型,考虑使用C++ s

《Effective C++》重点摘要(一)

开篇的话 这个星期不再发布关于数据结构的博客,想把半个月来看的书做一些总结,整理整理,第一本就是<Effective C++>.第一次看这本书是一年多前,准备考研复试的时候,随后陆陆续续,这个月再来看算是第三遍了吧,之前没有看过<深度探索C++对象模型>,所以有的地方看得不是很透彻(现在有的地方也看得不透,但是比以前好点了).还有另一本书就是<深度探索C++对象模型>,看第二遍,吸收得更容易了,看书总是这样,第一遍也许什么也不知道,但是只要坚持看下去,回过头来再读的时候

《Effective C++》重点摘要(八)

<Effective C++>第八章:定制new和delete 了解new-handler的行为.new和delete不是函数,是申请和释放内存的操作符.当new提出获得内存申请失败时会发生什么?老旧的编译器是返回null指针.现在呢,如果申请失败,会先调用一个错误处理函数,那就是new-handler.这就像一个回调函数,系统有一个默认的,用户也可以自行编写一个错误处理函数并使用set_new_handler函数设置之.通常自行编写的错误处理函数可以使用这些策略: 1) 多次尝试申请内存.

《Effective C++》重点摘要(九)

<Effective C++>第九章:杂项讨论 不要轻忽编译器的警告.严肃对待编译器发出的警告信息.努力在最高警告级别下争取无警告.也不要过度依赖编译器给出的警告信息,不同的编译器给出的警告信息是不一样的,甚至给出警告的点也不一样. 让自己熟悉包括TR1在内的标准程序库. 让自己熟悉Boost. C++不想Java和C#那样,语言本身就带有很强大的库,所以外部的程序库对于编写C++程序至关重要.俗话说不要重复发明轮子嘛,所以能用别人的就用吧.当然这是在实际工程中,学习阶段嘛,我还是比较推崇自己

《Effective C++》重点摘要(三)

<Effective C++>第三章:资源管理 以对象管理资源.一份资源,可能是一片内存,可能是一个锁,当客户申请后需要手动释放才是合理的,那么最好在获得这份资源的时候,就立刻把它放到一个对象里(RAII技术),然后在对象的析构函数里释放它.这样释放操作就不会被遗忘了,并且,就算在使用资源类的语境中有语句抛出异常,也能确保资源得到正确的释放. 在资源管理类中小心coping行为.有的资源是不支持copy语义的,而有的资源需要深拷贝语义,有的资源支持控制权转移语义,还有的资源是支持引用计数的,所

《Effective C++》重点摘要(七)

<Effective C++>第七章:模板与泛型编程 了解隐式接口和编译期多态.面向对象编程总是采用显式地声明一个接口,并在子类中提供特殊的实现,进而实现运行期多态.模板类中的接口往往是隐式的,隐式的意思是,编写模板时,假设需要的接口类T中是存在的,至于实例化模板的类是否真的具有该接口,编译时才知道(如果实例化类型没有实现这些接口,则编译失败).这正是模板多态的展现方式,由实例化模板的类决定具体的行为是什么.这种多态编译期与运行期多态有很大的区别,运行期的多态是一种is-a类型的多态(我自造的

《More Effective C++》重点摘要一:基础议题

仔细区别pointers和references.指针和引用有些相似,他们本身都是对存在于某个地方的对象(不是指class)的指示,但是他们有着本质的区别.指针变量存储所指对象的地址,所指的对象可以是null,只要可以寻址就行.而引用是某个已经存在对象的别名,所以不可以先声明一个引用,经过一段时间(代码)后让它指向某个对象. 最好使用C++转型操作符.C++提供了自己的四种转型操作符: 1) static_cast.拥有与C旧式转型相同的意义与限制,但不能移除表达式的常量性,因为有const_ca