1.1 条款1:视C++为一个语言联邦(View C++ as a federation of language)
C++主要的次语言(sublanguage):
l 【3-1-1】 说到底C++仍是以C为基础。区块(blocks),语句 (statements),预处理器(preproccessor),内置数据类型(built-in data types),数组(arrays),指针(pointers)等都来自于C。
l 【3-1-2】 Object-Oriented C++.类classes,封装(encapsulation),继承(inheritance),多态(polymorphism),virtual函数(动态绑定)等等
l 【3-1-3】 Template C++这是泛型编程(genericprogramming)部分,它带来了崭新的编程范型(programming paradigm),也就是所谓的template metaprogramming(TMP,模板元编程)。
l 【3-1-4】 STL是个template程序库。它对容器(containers),迭代器(iterators),算法(algorithms)以及函数对象(function objects)。
1.2 条款2:尽量使用consts,enums和inlines,少用#define (Prefer consts,enums and inlines to #define)
在C++程序中,尽量使用consts,enums和inlines,少用#define。
l 【3-2-1】对于单纯常量,最好以const对象或enum替换#defines。
l 【3-2-1】对于形似函数的宏(macros),最好改用inline函数。
例如:
const double PAI = 3.1415;
const char* const HELLO = "Hello";
const std::string AUTHOR_NAME = "wuzhenghua";
class CAnimal
{
Private:
const int COLOR = 1;
};
示例3-2-1 常量
我们无法利用宏#define创建一个class专属常量,因为#define并不重视作用域(scope)。
一个属于枚举类型的数值可以冒充ints使用。枚举的行为比较像#define而不像const。例如,取一个const的地址是合法的,但是取一个enum的地址就不合法,而取#define的地址也是不合法的。
#define函数一是语句引起的歧义,二是没有安全类型检查。所以最好使用inline函数,如下:
template<typename T>
Inline void MAX(const T& a, const T &b)
{
f(a > b ? a:b);
}
示例3-2-2 inline函数
1.3 条款3:尽可能使用const(Use const wheneverpossible)
Const的一件奇妙事情是,它允许你指定一个语义约束(也就是指定一个“不被改动”的对象),而编译器会强制实施这项约束。
1.3.1 const与指针
面对指针,修饰指针,或修饰指针所指物。如果关键值const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示两者都是常量。
char greeting[]="Hello";
char *p = greeting; //non-const pointer, non-const data
const char *p = greeting; //non-const pointer, nconst data
char * const p = greeting; //const pointer, non-const data
const char * const p = greeting; //const pointer, const data
示例3-3-1 const与指针变量
1.3.2 const与迭代器
如果你希望迭代器所指的东西不可被改动(即是希望STL模拟一个const T* 指针),你得使用const_iterator:
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); // iter像T*const
*iter = 10; //正确
iter++; //错误
std::vector<int>::const_iterator cIter = vec.begin();// iter像const T*
*cIter = 10; //错误
cIter++; //正确
示例3-3-2 const与迭代器
令函数返回一个常量值,可以降低因客户错误而造成的意外,而不置于放弃安全性和高效性。
class Rational {…};
const Rational operator*(const Rational& lhs, const Rational & rhs);
Rational a,b,c; //正确
if(a*b=c) //其实想做一个比较而掉了一个=,编译器报错
示例3-3-3 const与operator*
1.3.3 const与成员函数
理由:第一,const是class接口比较容易被理解;第二,const使“操作const对象”成为可能。两个成员函数如果只是常量性(constness)不同,可以被重载。这是C++的一个重要特性。const成员函数不可以更改对象内任何non-static成员变量。
const与non-const成员函数应该避免代码重复。
Class TextBlock {
public:
…
const char & operator[](std::size_t pos) const
{
…
}
char & operator[](std::size_t pos)
{
return const_cast<char &> //将op[]返回值的const去除
(static_cast<const TextBlock &>(*this) //为*this加上const
[pos];
}
…
}
示例3-3-4 const与成员函数
【注意】 non-const成员函数本来就可以对其对象做任何动作,所以在其中调用一个const成员函数并不会带来风险。
1.4 条款4:关于“将对象初始化”这事,C++似乎反复无常(Make sure that object areinitialized before they’re used)
关于“将对象初始化”这事,C++似乎反复无常。
确保每个构造函数都将对象的每一个成员初始化。但是,别混淆赋值(assignment)和初始化(initiation)。
class ABEntry{
public:
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones );
private:
std::string theName;
std::list<PhoneNuber> thePhones;
int numTimesConsulted;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ){ //这些都是赋值
theName = name;
thePhones = phones;
numTimesConsulted = 0;
}
ABEntry(const std::string &name, const std::list<PhoneNuber> &phones ): theName(name), thePhones(phones), numTimesConsulted(0)
{ //这些都是初始化
}
示例3-4-1 赋值与初始化
C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。成员初始化列(memberinitialization list)比赋值效率高,但是内置类型的效率是一样的。
一旦你已经很小心将“内置型成员变量”明确地加以初始化,而且也确保你的构造函数运用“成员初始列”初始化base class和成员 变量,那就只剩下“不同编译单元内定义之non-conststatic对象”的初始化次序。
所谓static对象,其寿命从被构造出来直到程序结束为止。这种对象包括global对象,定义于namespace作用域内的对象,在class内,在函数内,以及在文件作用域内被声明static的对象。
函数内的static对象称为local static对象,其他称为non-local static对象。
所谓编译单元(translationunit)是指产出单一目标文件(single object file)的那些源码。
C++对定义于不同编译单元内的non-local static对象的初始化次序没有明确定义。
可以解决该问题的方法:将每个non-localstatic对象搬到自己的专属函数内(该对象在此函数声明为static)。这些函数返回一个reference指向它所包含的对象。然后,用户调用这些函数,而不直接涉及这些对象。
C++保证:函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。