条款01:视C++为一个语言联邦 C:说到底C++仍是以C为基础。
Object-Oriented C++:这部分也就是C with Classes所诉求的:classes(包括构造函数和析构函数),封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)……等等。
Template C++:这是C++的泛型编程部分,也是大多数程序员经验最少的部分。
STL:STL是个template程序库,看名称也知道,但它是非常特殊的一个。
条款02:尽量以const,enum,inline 替换 #define 这个条款或许改为“宁可以编译器替换预处理器”比较 ,因为或许#define不被视为语言的一部分。那正是它的问题所在。当你做出这样的事情:
#define ASPECT_RATIO 1.653
记号名称ASPECT_RATIO也许从未被编译器看见:也许在编译器开始处理源码之前它就被预处理器移走了。于是记号名称ASPECT_RATIO有可能没进入几号表内。
解决之道是以一个常量替换上述的宏(#define):
const double AspectRadio = 1.653;
作为一个语言常量,AspectRadio肯定会被编译器看到,当然就会进入记号表内。
当我们以常量替换#define,有两种特殊情况值得说说。
第一是定义常量指针。
const char* const authorName = "Scott Meyers";
string对象通常比其前辈char*-based合亦,所以上述的authorName往往定义成这样更好些:
const string authorName("Scott Meyers");
第二值得注意的是class专属常量。为了将常量的作用域限制于class内,你必须让它成为class的一个成员;而为此确保此常量至多只有一份实体,你必须让它成为一个static成员:
class GamePlayer
{
private:
static const int NumTurns = 5;
int scores[NumTurns];
......
};
但如果你取某个class专属的地址,或纵使你不取其地址而你的编译器却坚持要看到一个定义式,你就必须另外提供定义式如下:
const int GamePlayer::NumTurns;
请把这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值,因此定义时不可以再设初值。
如果你的编译器不允许static整数型class常量完成in class初值设定,可改为所谓的the enum hack补偿做法,即:
class GamePlayer
{
private:
enum{NumTurns = 5};
int scores[NumTurns];
};
如果你不想让别人获得一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。
enums和#defines一样绝不会导致非必要的内存分配。
#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被累加一次
在这里,调用f之前,a的递增次数竟然取决于“它被拿来和谁比较”
你可以获得宏带来的效率以及一般函数的所有可预料行为和类型安全性
template <typename T>
inline void callWithMax(const T& a, const T&b)
{
f(a > b ? a : b);
}
有了consts、enums和#inlines,我们对预处理器的需求降低了,但并非完全消除。#include仍然是必需品,而#ifdef/#ifndef也继续扮演控制编译的重要角色。目前还不到预处理器全面引退的时候,但你应该明确地给予它更长更频繁的假期。
条款03:尽可能使用const 面对指针,你也可以指出指针自身、指针所指物、或两者都(或都不)是const:
char greeting[] = "Hello";
char* p = greeting; //non-const pointer,non-const data
const char* p = greeting; //non-const pointer,const data
char* const p = greeting; //const pointer,non-const data
const char* const p = greeting; //const pointer,const data
STL迭代器系以指针为根据塑模出来,所以迭代器的作用就像个T*指针。声明迭代器为const就像声明指针为const一样(即声明一个T* const指针),表示这个迭代器不得指向不同的东西,但它所指的东西的值是可以改动的。如果你希望迭代器所指的东西不可改动(即希望STL模拟一个const T*指针),你需要的是const_iterator:
vector<int> vec;
const vector<int>::iterator iter = vec.begin();
*iter = 10;
++iter;//错误
vector<int>::const_iterator cIter = vec.begin();
*cIter = 10;//错误
++cIter;
const最具威力的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值、各参数、函数自身(如果是成员函数)产生关联。
class Rational{};
const Rational operator* (const Rational &lhs, const Rational &rhs);
为什么返回一个const对象?原因是如果不这样客户就能实现这样的暴行:
Rational a, b, c;
(a * b) = c;
或者
if (a * b = c)//其实是想做一个比较动作!
bitwise constness和logical constness
bitwise const阵营的人相信,成员函数只有在不更改对象之任何成员变量(static除外)时才可以说是const。也就是说它不更改对象内的任何一个bit。这种论点的好处是很容易侦测违反点:编译器只需寻找成员变量的赋值动作即可。
class TextBlock
{
public:
const char& operator[] (size_t position)const
{
return text[position];
}
char& operator[] (size_t position)
{
return const_cast<char&>(static_cast<const TextBlock&>(*this));
}
};
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
posted on 2012-11-04 21:20
zhuxin 阅读(101)
评论(0) 编辑 收藏 引用 所属分类:
Effective C++