当前位置: 首页 > article >正文

《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 定义常量,例如:

#define PI 3.14

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

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

const double pi = 3.14;

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

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

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

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

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

// 对较大者调用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 可以兼顾效率和安全性:

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 对象

考虑下面的情形:

class A {
public:
	void f();
};

extern A a;		// 预留给客户使用的对象
A a;
// 客户的文件
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 对象的初始化放入函数中:

A& init(){	// 第一行定义static对象,第二行返回,可以考虑定义为inline
	static A a;
	return a;
}
class B {
	void g() {
		init().f();
	}
};

http://www.kler.cn/news/309971.html

相关文章:

  • SpringDataJPA基础增删改查
  • 【OpenGL 学习笔记】01 - CLion 配置 CMake 运行初始 demo
  • 【PyTorch入门·求导相关】一文解释 PyTorch的求导 (backward、autograd.grad)
  • C#中抽象类和接⼝有什么区别?
  • 深入解析:联邦政策如何影响科技行业发展
  • windows10通过coursier安装scala
  • 什么是注解?
  • 硬件工程师笔试面试——电机
  • 25. MyBatis中的RowBounds是什么?如何实现内存分页?
  • Presto如何使用
  • CF EDU 169
  • 初写MySQL四张表:(1/4)
  • 【Kubernetes】常见面试题汇总(二十三)
  • Longman Dictionary of Contemporary English (朗文当代高级英语辞典)
  • MySQL高阶1783-大满贯数量
  • 使用python 将world的题库导入某学习软件的模板
  • 【RabbitMQ】工作模式
  • 20240911软考架构-------软考156-160答案解析
  • 【物联网】深入解析时序数据库TDengine及其Java应用实践
  • 力扣最热一百题——合并两个有序链表
  • do { ... } while (0) 的意义
  • 单片机拍照_将采集的RGB图像封装为BMP格式保存到SD卡
  • Pandas_sqlite
  • bestphp‘s revenge1
  • 排序----数据结构
  • 9.18日常记录
  • cmd修改游戏数据处理量大小
  • vue获取最近7天时间;获取任意时间段时间
  • 【前端】main.js中app.vue中 render函数的作用及使用背景
  • 【数据库】MySQL-基础篇-事务