第1
章:让自己习惯C++
条款01
:视C++
为一个语言联邦
- 将
C++
视为由4
个次语言组成的联邦:C
:没有模板、没有异常、没有重载…Object-Oriented C++
:类、封装、继承、多态、虚函数、动态绑定等等;Template C++
:泛型编程部分;STL
:是一个Template
程序库,容器、迭代器、算法以及函数对象。
从某个此语言切换到另一个时,高效编程守则可能会发生变化。比如,C-like
类型(内置类型)pass by value
更好;对于Object-Oriented C++
而言,pass by reference to-const
更好;再切换到STL
,由于迭代器和函数对象都是在C
指针之上塑造出来的,pass by value
守则再次适用。
条款02
:尽量以const
,enum
,inline
替换#define
对于单纯常量,最好以
const
对象或enum hack
替换#define
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 记号名称未进入符号表, 难以调试
// 在多处出现目标码, 尤其浮点常量
// 无作用域概念, 不提供任何封装性
// 常量会被编译器看到, 进入符号表
// 导致较小量的目标码, 因为只有一份
// 可放置在类内或 namespace 中限制其作用域
const double AspectRatio = 1.653;
// 可以放在类或 namespace 中
// 令 NumTurns 成为 5 的记号名称, 一般用在类中做常量用
// 行为像 #define, 对 const 取地址合法,对 enum 和 #define 取地址就不合法
enum {NumTurns = 5};对于形似函数的宏,最好用
inline
(或模板)函数替换宏中的变量有可能会被运算多次。
条款03
:尽可能使用const
const
作用于迭代器STL
迭代器是以指针为根据塑模出来的,其作用就像个T*
指针。声明迭代器为const
只是声明一个const
指针(作用就像T* const
),表明的是迭代器本身不可变,但其所指的值是可以改动的。如果希望迭代器所指的值不可改动,需要的是const_iterator
。1
2
3
4
5
6
7
8
9std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin();
*iter = 10; // 正确
++iter; // 错误
std::vector<int>::const_iterator citer = vec.begin();
*citer = 10; // 错误
++citer; // 正确将
operator*
的返回类型声明为const-by-value
const
可被施加于任何作用域内的对象、函数参数(常用pass-by-reference-to-const
),函数返回类型、类成员函数本体const
施加于成员函数成员函数上的
const
限定符意味着不能修改non-mutable
,non-static
类数据成员。当
const
和non-const
成员函数有着实质等价的实现时,令non-const
成员函数调用const
版本可避免代码重复1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class TextBook
{
public:
...
const char& operator[](std::size_t position) const
{
... // 边界检验
... // 日记数据访问
... // 检验数据完整性
return text[position];
}
/*
char& operator[](std::size_t position)
{
... // 边界检验
... // 日记数据访问
... // 检验数据完整性
return text[position];
}
*/
char& operator[](std::size_t position)
{
// 调用 const_cast 移除对象身上的 const
// 调用 static_cast 为 *this 加上 cosnt
return const_cast<char &>(
static_cast<const TextBook&>(*this)[position]
);
}
private:
std::string text;
};const
版本成员函数调用non-const
版本不合法
条款04
:确定对象被使用前已先被初始化
为内置型对象进行手工初始化,因为
C++
并不保证初始化它们构造函数使用成员初始化列表,初始化顺序与在类中声明顺序一致
为避免“跨编译单元内定义的
non-local static
对象的初始化问题”,以local
对象替换non-local static
对象static
对象包括global
对象、定义与namespace
作用域内的对象、在class
内、函数内、以及在file
作用域内被声明为static的对象- 函数内的
static
被称为local static
对象,其它static
对象被称为non-local static
对象 - 程序结束时,
static
对象会被自动销毁,也就是它们的析构函数在main
函数结束时被自动调用 - 编译单元是指产出单一目标文件的源码文件以及所含入的头文件
现在有两个源码文件,每个至少含入一个non-local static
对象,其中一个non-local static
对象用到了另一个non-local static
对象,而被用到的尚未被初始化。
1 | // 文件 FileSystem.cpp 中 |
现在客户决定创建一个Directory对象,用来放置临时文件:
1 | Directory tempDir; // 为临时文件而做出的目录 |
这个时候就会出现初始化次序带来的问题。由于tfs
和tempDir
是不同的人在不同的时间于不同的源码文件中创建出来的,因此初始化次序不一定。
解决方法:
1 | // 文件 FileSystem.cpp 中 |