条款01:视C++为一个语言联邦
将C++视为由四个次语言组成的联邦,每一个次语言都有自己的守则,跨越语言时,需要改变规则:
- C:C++以C为基础。区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据类型(build_in data types)、指针(pointer)等统统来自C
- Object-Oriented C++:这部分也是C with Classes 所诉求的:classes、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)
- Template C++:这是C++泛型编程(generic programming)部分
- STL:STL是个template程序库。它对容器(containers)、迭代器(iterators)、算法(algorithm)以及函数对象(function objects)的约束有极佳的紧密配合和协调
C++高效编程守则视状况而变化,取决于你使用那一部分
条款02:尽量以const、enmu、inline替换#define
#define ASPECT_RATIO 1.653
记号名称ASPECT_RATIO可能从未被编译器看见;也许在编译器开始处理源代码之前它就被预处理器移走了。于是记号名称可能没有进入记号列表(symbol table)内。
1.const:
可以用一个常量替换上述的宏(#define):
const double AspectRatio=1.653
作为语言常量,AspectRatio肯定会被编译器看见,然后进入记号表。
以常量替换#define,有两种特殊情况:
- 定义常量指针
由于常量定义式通常放在头文件内,因而有必要将指针(不是指针所指之物)声明为const
- class专属常量
为了将常量的作用域(scope)限制于class内,你必须让它成为class的一个成员(member);而为了确保此常量至多只有一个实体你必须让它成为一个static成员:
class GamePlayer{
private:
static const int NumTurns=5; //常量声明式,类成员不能在定义时初始化
int score[NumTurns];
};
通常C++要求你对你所使用的任何东西提供一个定义式,但如果它是一个class专属常量又是static且为整数类型(例如int、char、bool),则需要特殊处理——只要不取它们的地址,你可以声明并使用他们而无需提供定义式。反之,你就必须提供一个定义式,如下:
const int Gameplayer::NumTurns; //NumTurns的定义,由于在声明时已获得初始值,因此无需再次赋值
顺带一提,我们无法用#define创建一个class专属常量,因为#define并不重视作用域。一旦定义,一直有效(除非在某处被#undef)。这意味着#define不能定义class专属常量,也不能够提供任何封装性,即没有private#define。
2.enum:
class GamePlayer{
private:
enum{NumTurns=5}; //常量声明式,类成员不能在定义时初始化
int score[NumTurns];
};
3.inline:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
int a=5,b=0;
CALL_WITH_MAX(++a,b); //a被累加两次
CALL_WITH_MAX(++a,b+10) //a被累加一次
解决这个问题的办法:
template<typename T> call_with_max(const T &a,const T &b)
{
f(a>b?a:b);
}
对于形似函数的宏(macros),最好改用inline函数替换#define;
条款03:尽可能使用const:
const出现在*左侧,表示被指物是常量;如果出现在*右侧,表示指针自身是常量;如果出现在*两边,表示被指物和指针两者都是常量。
如果被指物是常量,可以将关键字const写在类型之前或之后,两种写法意义相同:
const Widget *pw;
Widget const *pw; //两者意义相同
const最具威力的用法是面对函数声明时的应用,例如,令函数返回一个常量值可以降低因客户错误而造成意外:
class Rational{};
const Rational operater*(const Rational &lhs,const Rational &rhs);
Rational a,b;
if(a*b=c); //其实是想做一个比较动作
const成员函数:
class TexBlock{
public:
const char& operation[](std::size_t position)const
{return text[position];} //operator[] for const 对象
char& operator[](std::size_t position)
{return text[position];} //operator[] for non_const对象
private:
std::string text;
};
TextBlock tb("hello");
const TextBlock ctb("world");
std::cout<<tb[0]; //读一个non_const TextBlock
tb[0]='x'; //写一个non_const TextBlock
std::cout<<ctb[0]; //读一个const TextBlock
ctb[0]='x'; //错误!写一个const TextBlock
tb[0]='x';
成员函数为const的意义
对于这个问题有两个流行概念:bitwise constness和logical constness
bitwise constness:成员函数只有在不更改对象之任何成员变量时才可以说是const
这种观点的好处是很容易侦测违反点:编译器只需寻找成员变量的赋值动作即可。bitwise constness正是C++对常量性(constness)的定义,因此const函数不可以更改对象内任何
non_static成员变量
。
class CTextBlock{
public:
char& operation[](std::size_t position)const
{return pText[position];}
private:
char *pText;
};
const CTextBlock cctb("hello");
char *pc=&cctb[0];
*pc='x';
logical constness:一个const成员函数可以修改所处理对象的某些bits,但只有在客户端侦测不出的情况下才得如此
class CTextBlock{
public:
std::size_t length()const;
private:
char *pText;
std::size_t text_length;
bool lengthIsValid;
};
std::size_t CTextBlock::length()const
{
if(!lengthIsValid)
{
text_length=std::strlen(pText); //错误
lengthIsValid=true; //错误,在const成员函数内不能对其赋值
}
return textLength;
}
class CTextBlock{
public:
std::size_t length()const;
private:
char *pText;
mutable std::size_t text_length;
mutable bool lengthIsValid; //这些成员总是会被改变,即使是在const成员函数内
};
std::size_t CTextBlock::length()const
{
if(!lengthIsValid)
{
text_length=std::strlen(pText);
lengthIsValid=true; //编译通过
}
return textLength;
}
在const和non_const成员函数中避免重复
class TextBlock{
public:
const char& operator[](std::size_t position)const
{
... //bounds checking
... //log access data
... //verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
... //bounds checking
... //log access data
... //verify data integrity
return text[position];
}
private:
std::string text;
};
一般而言,转型(casting)是一个糟糕的想法,详见条款27。
class TextBlock{
public:
const char& operator[](std::size_t position)const
{
... //bounds checking
... //log access data
... //verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
return
const_cast<char&>( //将operator[]返回值的const转除
static_cast<const TextBlock&>(*this) //将*this(non_const TextBlock)转换为const
[position]); //调用const operator[]
}
private:
std::string text;
};