《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();
	}
};
相关推荐
鱼鱼说测试1 小时前
postman接口自动化测试
开发语言·lua
從南走到北1 小时前
JAVA国际版东郊到家同城按摩服务美容美发私教到店服务系统源码支持Android+IOS+H5
android·java·开发语言·ios·微信·微信小程序·小程序
_不会dp不改名_1 小时前
C++ 20: Concepts 与Requires
开发语言·c++20
韭菜钟2 小时前
Qt从qmake迁移到cmake的记录
开发语言·qt
重启的码农2 小时前
llama.cpp 分布式推理介绍(7) 远程后端缓冲区 (RPC Buffer)
c++·人工智能·神经网络
Vect__2 小时前
链表漫游指南:C++ 指针操作的艺术与实践
数据结构·c++·链表
少陵野小Tommy2 小时前
Python能用古诗词数据库做什么7:根据标题、诗句查找诗歌
开发语言·数据库·python
长城20242 小时前
PHP如何使用JpGraph生成3D饼形图?
开发语言·php·jpgraph·3d饼形图
saltymilk3 小时前
C++ 使用分治减小模板递归深度
c++
悠哉清闲4 小时前
C ++代码学习笔记(一)
c++·笔记·学习