《Effective C++》第三版——让自己习惯C++

参考资料:

  • 《Effective C++》第三版

注意:《Effective C++》不涉及任何 C++11 的内容,因此其中的部分准则可能在 C++11 出现后有更好的实现方式。

条款 1:视 C++ 为一个语言联邦

可以将 C++ 视作一个由多个次语言 构成的语言联邦,不同次语言 可能有不同的高效编程守则

C++ 中主要的次语言有 4 个:C、Object-Oriented C++、Template C++、STL。

条款 2:尽量以 constenuminline 替换 #define

对于单纯常量,最好以 const 对象或 enums 替换 #define

通过 #define 定义常量,例如:

cpp 复制代码
#define PI 3.14

PI 将在预处理阶段 被"移走",导致 PI 没有进入记号表、不能被编译器 看见,这可能给后续调试带来麻烦。

使用 const 替换 #define,可以确保名字能被编译器看到:

cpp 复制代码
const double pi = 3.14;

此外,使用 const 还能实现可以将常量的可见范围限定在某个作用域(如 class 内部),而 #define 不具备此功能。

在某些特殊情况里,我们也可以用 enum 实现 int 常量:

cpp 复制代码
class A{
private:
	// 这里的特殊情况指,某些编译器(错误地)不允许为static整型class常量提供类内初始值
	enum { Num = 5 };
	int arr[Num];
}

对于形似函数的宏,最好以 inline 函数替换 #define

使用 #define 定义宏可能会导致很多问题:

cpp 复制代码
// 对较大者调用f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))	// 已经足够小心地为每个实参添加小括号

int a = 5, b = 0;
CALL_WITH_MAX(++a, b);	// a累加2次
CALL_WITH_MAX(++a, b+10);	//a累加1次

利用 template inline 可以兼顾效率和安全性:

cpp 复制代码
template<typename T>
inline void callWithMax(const T &a, const T &b) {
	f(a > b ? a : b);
}

条款 3:尽可能使用 const

将某些东西声明为 const 可帮助编译器侦测出错误用法

  • 对于指针,const 出现在 * 前代表被指物是常量,出现在 * 后代表指针本身是常量;
  • 对于迭代器,用 const 修饰一个迭代起类型,代表这个迭代器是常量,const_iterator 代表被指物是常量;
  • 对于函数参数,如果不要在函数内部对其进行修改,就将它们声明为 const

编译器强制实施 bitwise constness,但编写程序时还要注意 conceptual constness

设计 const 成员函数,不仅可以明确 地表示哪些成员函数可以改动对象内容 ,哪些不行,还可以使操作 const 对象成为可能。

需要注意的是,对于 const 成员函数,编译器只保证 bitwise constness ,即不更改对象的任何成员。然而,如果对象中包含指针,编译器却允许 const 成员修改指针所指的内容。另外,如果我们希望绕过 bitwise constness,可以将成员声明为 mutable

constnon-const 成员函数功能相似时,令 non-const 版本调用 const 版本可以减少代码重复

constnon-const 成员函数功能相似时,不应令 const 版本调用 non-const 版本,因为这会带来"对象被修改"的风险;而应令 non-const 版本调用 const 版本。

条款 4 确定对象被使用前已先被初始化

对内置对象进行手工初始化,因为 C++ 不保证初始化它们

C++ 的初始化规则较为复杂,只需要记住所有对象使用之前必须初始化,对于内置类型 ,必须手工完成此事

构造函数最好是用成员初值列,顺序应与其在 class 中的声明顺序相同

为避免跨编译单元初始化次序问题,应以 local static 对象代替 non-local static 对象

考虑下面的情形:

cpp 复制代码
class A {
public:
	void f();
};

extern A a;		// 预留给客户使用的对象
A a;
cpp 复制代码
// 客户的文件
extern A a;

class B {
	void g() {
		a.f();
	}
};

B b;
b.g();	// 不能确保g()被调用前,a已经初始化

C++ 对于定义在不同编译单元non-local static 对象的初始化顺序并无明确定义 ,所以无法确保 b.g() 执行前 a 已经被初始化。

解决方法是,将 non-local static 对象的初始化放入函数中:

cpp 复制代码
A& init(){	// 第一行定义static对象,第二行返回,可以考虑定义为inline
	static A a;
	return a;
}
cpp 复制代码
class B {
	void g() {
		init().f();
	}
};
相关推荐
q5673152315 分钟前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
JSU_曾是此间年少27 分钟前
数据结构——线性表与链表
数据结构·c++·算法
许野平40 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨43 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
狂奔solar1 小时前
yelp数据集上识别潜在的热门商家
开发语言·python
此生只爱蛋1 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧2 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong2 小时前
Java反射
java·开发语言·反射