条款02:尽量以const,enum,inline替换#define
#define ASPECT_RATIO 1.653
上述定义由预处理器处理,因此记号ASPECT_RATIO可能没进入记号表(symbol table)内。于是当你运用此常量但获得一个编译错误时,可能会带来困惑,因为错误提到的是1.653而不是ASPECT_RATIO。
解决的办法是以一个常量替换上述的定以:
const double AspectRatio = 1.653;
使用常量可能比使用#define导致较小量的代码,因为预处理器“盲目地将名称ASPECT_RATIO替换为1.653”可能导致目标码出现多份1.653,而改用常量绝不会出现相同情况。Class内的常量定义(Static是为了确保此常量至多只有一份实体):
static const int NumTurns = 5;//常量声明式
旧式编译器可能不允许static成员在其声明式上获得其初值。此外所谓的“in-class初值设定”也只允许对整数常量进行。因此此时只能在定义式上设定初值。有时我们声明一个类时,需要使用到这些常量,但常量又没有设定初值,此时可以使用enum(枚举类型):
class Gameplayer {
private:
enum {NumTurns = 5}; //NumTurns可以当作int型(5)被使用
int scores[Numturns]; //
}
#define的另一个误用情况是以它实现宏(macros)。它看起来像函数,但不会招致函数调用带来的额外开销。然而宏定义因其繁琐的写法,导致宏并不受欢迎,为了能获得宏带来的效率以及一般函数的所有可预见行为和类型安全,可用template inline函数代替(见条款30):
template<tyoename T>
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a:b);
}
有了const、enum和inline,我们对预处理器(特别是#define)的需求降低了,但并非完全消除,#include任然必须,而#ifdef/#ifndef也扮演着控制编译的重要角色。
条款03:尽量使用const
const指定一个语义约束,也就是指定一个“不被改动的对象”,而编译器会强制实施这项约束。Const出现在星号的左边表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果两边都有,表示被指物和指针两者都是常量。
将const实施于成员函数的目的,是为了确认该成员函数可作用与const对象身上。第一,const成员函数不能修改对象内容。第二,它们使“操作const对象”成为可能。改善c++程序效率的一个根本办法是以pass-by-reference-to-const方式传递对象,而此技术的前提,是有const成员函数可用来处理取得的const对象:
class TextBlock {
public:
...
const char & operator[](std::size_t position) const {
return text[position];
}
char &operator[](std::size_t position) const {
return text[position];
}
std::string text;
};
void print(const TextBlock& ctb) { //
std::cout << ctb[0]; // 调用const TextBlock::operator[]
}
non-const operator[]的返回类型是个reference to char,不是char。如果返回一个char,下面这样的语句就无法通过编译:
tb[0] = 'x';
因为,如果函数的返回类型是个内置类型,那么改动函数返回值从来都不合法。纵使合法,以by value返回对象这一事实意味被改动的其实是tb.text[0]的一个副本,而不是其自身。
一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针隶属于对象,那么称此函数为bitwise const不会引发编译器异议。这会导致:
class CTextBlock {
public:
...
char &operator[](std::size_t position) const //bitwise const声明
{ return pText[position];}
private:
char* pText;
};
上述operator[]实现代码并更改pText。因此能通过编译。但是他允许以下事情发生:
const CTextBlock cctb(“Hello”); //声明一个常量对象
char* pc = &cctb[0]; //调用const operator[]取得一个指针,指向cctb的数据。
*pc = ‘J’; //cctb现在有了“Jello”这样的内容。
这其中当然不会有任何错误,因为并没有改变常量对象的成员变量的值,只是某个指针成员所指向的内容发生了改变,这不违背bitwise constness,所以编译器不会出现异议。
当const成员函数必须要修改某些成员变量时,此时可以利用const相关的摆动场:mutable(可变的)。Mutable释放掉non-static成员变量的bitwise constness约束。
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复(需要进行转型):
Class TextBlock {
public:
...
const char & operator[](std::size_t position) const //一如既往
{
...
...
return text[position];
}
char & operator[](std::size_t position) //现在只调用const op[]
{
return
const_cast<char&>( //将op[]返回值的const转除
static_cast<const TextBlock&>(*this) //为*this加上const
[posiition]); //调用const op[]
}
...
};