条款1:视C++为一个语言联邦
C++的四个次语言部分:C, Object-oriented c++, template c++, STL
请记住:
C++ 高效编程守则视情况而变化,取决于你使用 C++ 的那个部分。
条款2:尽量以const, enum, inline替换#define
练习代码
// 含有数组元素的类
class ArrData {
public:
ArrData() = default;
ArrData(int ip, int jp) : i(ip), j(jp), arr{1, 2, 3}, vec(3, 0) {}
private:
int i;
int j;
int arr[5];
vector<int> vec;
// static int snum = 0; // 错误,必须在类外定义,类里面声明
static int snum; // 仅声明,right
static const int scnum = 0; // right,仅有const整型static成员可以在类里面定义
const int cnum = 0; // right
int num = 0; // right
// static double sfactor = 0.0; // 错误,非整形的static成员均不能再类里面定义
static double sfactor; // 仅声明,right
// static const double scfactor = 0.0; // 错误,非整形的static成员均不能再类里面定义
static const double scfactor; // 仅声明,right
const double cfactor = 0.0; // right
double factor = 0.0; // right
};
int ArrData::snum = 0;
double ArrData::sfactor = 0.0;
const double ArrData::scfactor = 0.0;
请记住:
对于单纯常量,最好以const对象或者enums替换#define
对于形似函数的宏,最好改用inline函数替换#define
条款3:尽可能使用const
理解几种const的形式的意义
const Widget * pw; // pw指向内容不可修改
Widget const * pw; // pw指向内容不可修改
Widget * const pw; // pw指针不可修改,即不能指向其他内容
const Widget * const pw; // pw指向内容不可修改,且pw指针不可修改,即不能指向其他内容
vector<int> vec{1,2,3,4};
const auto iter = vec.begin(); // const iter, iter不可修改
++iter; // 错误,iter迭代器不可修改,类似于指针不可修改
auto citer = vec.cbegin(); // citer迭代器指向内容不可修改
const 对象只能调用const成员函数;
非const 对象可以调用任何成员函数;
const 成员函数可以访问任何成员变量,只要不修改即可(其实也可以修改,下面详述);
标准库中容器部分接口(不修改容器内容的比如empty(), size(),以及部分会修改内容的比如operator[], at()等)都提供了const 和non-const 两个版本的接口,这是C++的一个重要特性,两个函数如果只是常量性不同,可以被重载;
bitwise const: 成员函数只有在不修改任何成员变量时才可以说是const
logical const: const成员函数可以修改部分bits,但是只有在客户端侦测不出的情况下,可以用mutable实现。
可变数据成员,mutable, 使const成员函数也可以访问且修改其值;
const 和 non-const成员函数中避免重复:
1)可以使non-const 版本调用 const 版本,注意调用中的类型转换;
2)不应该使const 版本调用 调用non-const版本,这破坏了const不改变成员的规则;
// const版本[]
const char& operator[](size_t position) const {
....
return data[position];
}
// non-const版本[]
char& operator[](size_t position) {
....
// 注意,先转换为const object调用const版本,否则将陷入non-const的无穷无尽递归调用中
// 再调用const_cast去掉返回引用的const属性
return const_cast<char&>(static_cast<const Widget &>(*this)[position]);
}
请记住:
将某些东西声明为const可以帮助编译器检测错误用法,const 可用于任何作用域内的对象,函数参数,函数返回类型,成员函数等;
编译器强制实施bitwise const检测,但是你的代码应该根据业务施行logical const;
当const 和non-const成员函数有实质等价的实现时,令non-const版本调用const版本可避免代码重复;
条款4:确定对象被使用前已先被初始化
C中的数组不保证其内容被初始化,而C++中的vector容器却有此保证,对象调用默认构造函数,内置类型采用值初始化;
vector<int> vec(5); // 默认值初始化为0
vector<double> vec2(4); // 默认值初始化为0.0
成员变量的初值是由文件或者数据库读入等类似场景,应该抽取一个Init函数完成该功能,供所有构造函数调用,不应该在初值列中初始化。
static对象,其寿命从被构造出来直到程序结束为止,在函数内的对象称为local static对象,其余称为non-local static对象,包括:
1)global对象;
2)定义于namespace作用域内的对象;
3)在类里,函数里,文件作用域里声明为static的对象;
C++对定于与不同编译单元内的non-local static对象的初始化相对顺序并无明确定义。因为这是相当困难甚至无法完成的。
请记住:
为内置类型对象进行手工初始化,因为C++不保证初始化它们;
构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作,初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同(这点其实不一定,不同也没关系,但是要保证初值列都采用参数,不要让成员的初始化使用另一个成员,有前后以来关系);
为免除“跨编译单元的初始化次序”问题,请以 local static 对象替换 non-local static 对象