条款05:了解c++默默编写并调用了哪些函数

编译器可以暗自为 class 创建default构造函数,copy构造函数,copy assignment操作和析构函数
所有这些函数都是 public 并且 inline
编译器自动生成的析构函数是non-virtual
编写一个空类 class Widget{};
相当于:

class Widget{
public:
Widget() {...}
~Widget() {...}
Widget(const Widget& rhs) {...}
Widget& operator=(const Widget& rhs) {...}
};

注意:这些函数仅当被需要(被调用),他们才被编译器创建出来
自动生成的copy构造函数和 copy assignment操作符 只是单纯地将来源对象的每一个non-static 成员变量拷贝到目标对象

三法则:如果一个类需要编写显式析构函数,那么同时需要显式编写copy构造函数和 copy assignment操作符
如果一个类内含 reference成员变量 或内含 const 成员变量时,应该显式编写copy构造函数和 copy assignment操作符
因为 reference变量始终绑定在初始化时的变量之上,不允许更改绑定对象, const 变量也不能因为赋值操作被更改

如果其中声明了一个构造函数,编译器于是不再为它创建default构造函数

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

如果一个类中含有指针变量文件描述符(两者都是资源对象,内存资源和文件资源,具有 引用语义,即复制操作之后,具有引用语义的对象并没有与原对象分离,而是共同指向同一资源,注意区别于值语义对象)

如果该类具有引用语义,不能使用编译器自动生成的函数(copy构造函数和copy assignment操作符),那么就将这两个函数声明为private并且不予实现

class Widget{
private:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs);
};

如果发生Widget对象拷贝的情况,编译器会阻止,如果类内部成员函数调用这两个函数,链接器会报错(因为只有声明却没有定义)

注: boost::noncopyable

可以将下列宏定义置于 class的 private部分

#define DISALOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
TypeName& operator=(const TypeName&)
条款07:为多态基类声明 virtual 析构函数

当derived class 对象经由一个base class 指针被删除,且该base class 带着一个non-virtual 析构函数时,其结果未有定义。
原因:因为是base class 指针,所以指针按照 base class 的析构函数的方式进行解释(即使是该指针指向derived class,但是函数是non-virtual 的,多态不成立)
这样,derived class 内部继承而来的base class 部分被析构掉,而自身原有的部分仍然存在,造成了"局部销毁"
解决方法:给base class 一个 virtual 析构函数(利用多态)

任何 class 只要带有 virtual 函数都几乎确定应该也有一个 virtual 析构函数

所有的标准容器例如 vector,list,string,set,map都不带有 virtual 析构函数 且不作为 base class 使用
所以:禁止继承一个标准容器类或者带有non-virtual 析构函数的 class

条款08:别让异常逃离析构函数

析构函数绝对不要吐出异常(容易造成资源泄露),如果程序遭遇一个"于析构函数期间发生的错误"后无法继续执行,调用abort"强迫结束程序"是个合理选择,这样可以阻止异常从析构函数传播出去

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

derived class 对象内的base class 成分会在 derived class 自身成分被构造之前先构造妥当

对象在derived class 构造函数开始执行前不会成为一个derived class,此时对象会被解析至 base class对象,virtual 函数只会执行base calss对应的那份函数
如果base class 构造函数调用 virtual 函数时,此时 virtual 函数会被编译器解析至base class,而不会下降至derived class

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

这样就可以连续赋值了,比如 a  = b = c

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

这个协议不仅适用于标准赋值形式,也适用于所有赋值相关的运算,例如+=,*=

条款11:在 operator=中处理"自我赋值"

"自我赋值"发生在对象被赋值给自己时
一般而言,如果某段代码操作pointer或reference,而它们被用来"指向多个相同类型的对象",就需要考虑这些对象是否为同一个

class Base{...};
class Derived : public Base{...};
void DoSomething(const Base& rb, Derived* pd); //rb和*pd可能其实是同一对象(基类类型的指针或引用,可以引用基类类型对象,也可以引用派生类类型对象)

如果使用对象来管理资源,资源管理对象在copy发生时有正确的举措,自我赋值是安全的

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

上面赋值操作符中,*this 和 rhs有可能是同一对象,delete pb之后,rhs.pb会发生指向野指针

修改为:

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

copy and swap 技术:

pass by reference:

Widget& Widget::operator=(const Widget& rhs) {
Widget tmp(rhs);
swap(rhs);
return *this;
}

pass by value:

Widget& Widget::operator=(Widget rhs) {
swap(rhs);
return *this;
}
条款12:复制对象时勿忘其每一个成分

用户自定义的 copy函数必须复制对象内的每一个成员变量
在为derived class 编写 copy函数时,必须很小心的也复制其 base class 成分,那些成分往往是 private,所以让derived class 的copy函数调用相应的base class 函数的copy函数

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

copy函数应该确保复制"对象内的所有成员变量"和"所有base class成分"

EC++学习笔记(二) 构造/析构/赋值的更多相关文章

  1. Effective C++ 笔记二 构造/析构/赋值运算

    条款05:了解C++默默编写并调用哪些函数 编译器默认声明一个default构造函数.一个copy构造函数.一个copy assignment操作符和一个析构函数.这些函数都是public且inlin ...

  2. Effective C++笔记:构造/析构/赋值运算

    条款05:了解C++默默编写并调用哪些函数 默认构造函数.拷贝构造函数.拷贝赋值函数.析构函数构成了一个类的脊梁,只有良好的处理这些函数的定义才能保证类的设计良好性. 当我们没有人为的定义上面的几个函 ...

  3. ES6-个人学习笔记二--解构赋值

    第二期,解构赋值如果能够熟练应用确实是个十分方便的功能,但是过分的依赖和嵌套只会让代码理解和维护起来十分困难,是个体现高逼格的表达式呢~ 1,解构赋值的基础 //定义:es6运行按照一定模式,从数组或 ...

  4. 《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  5. 《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  6. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  7. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

  8. Typescript 学习笔记二:数据类型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  9. ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring

    接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...

随机推荐

  1. (十二)maven之nexus仓库的基本用法

    nexus仓库的基本用法 ① 启动nexus. 上一章有提到:https://www.cnblogs.com/NYfor2018/p/9079068.html ② 访问http://localhost ...

  2. Python之Mac Scrapy爬虫小记

    最近在尝试用Python爬虫,在装Scrapy的过程中遇到了一些麻烦. 上网搜索资料也未能解决command not found scrapy的报错. 最后我删除scrapy,用pip3.6 inst ...

  3. (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射

    http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...

  4. webpack之postcss集成

    项目 为了 兼容各个浏览器,需要加各种 c3前缀,如果手动的加肯定 相对比较麻烦,但是现在有webpack,gulp之类的 工具可以自动给我们加上,可以说效率上加速不少.如果 配置中 做个happyp ...

  5. Mysql,SqlServer,Oracle主键自动增长的设置

    在mysql中,如果把表的主键设为auto_increment类型,数据库就会自动为主键赋值.例如: CREATE TABLE google(id INT AUTO_INCREMENT PRIMARY ...

  6. C程序(2)

     

  7. 2017年网络空间安全技术大赛部分writeup

    作为一个bin小子,这次一个bin都没做出来,我很羞愧. 0x00 拯救鲁班七号 具体操作不多说,直接进入反编译源码阶段 可以看到,只要2处的str等于a就可以了,而str是由1处的checkPass ...

  8. SpringBoot整合升级Spring Security 报错 【The request was rejected because the URL was not normalized】

    前言 最近LZ给项目框架升级, 从Spring1.x升级到Spring2.x, 在这里就不多赘述两个版本之间的区别以及升级的原因. 关于升级过程中踩的坑,在其他博文中会做比较详细的记录,以便给读者参考 ...

  9. shell脚本,一个经典题目。

    [root@localhost wyb]# cat zhuijiu.sh #!/bin/bash #.写一个脚本执行后,输入名字,产生随机数01-99之间的数字. #.如果相同的名字重复输入,抓到的数 ...

  10. shell脚本,通过传参求斐波那契数列如(0,1,1,2,3,5,8,13..........)

    [root@localhost wyb]# cat fibo.sh #!/bin/bash #斐波那契数列 ,,,,,,, > file >> file count=$ for i ...