(一)前言
Effective C++是Scott Meyers编著,由侯捷老师翻译,也是STL源码剖析的作者。假期刚考完驾照,在家不能荒废学习,开始看看这本C++经典,并做一些笔记。
本书标题提到“改善程序与设计的55个具体做法”,对应书中的目录,可以看到分为9大章节,55个条款。几乎涵盖了C++的所有内容:面向对象,资源管理,模板,内存管理等等。
接下来将分条款学习记录。
(二)视C++为一个语言联邦
美帝作为联邦国家,他的50州都有自己的立法和行政机关,这里提到C++为一个语言联邦,是因为C++作为一个多重泛型编程语言,支持面向对象、泛型等特性。就像联邦政府一样,C++主要有4个次语言,当使用不同的次语言时,某些守则可能改变:
1.C
2.Object-Oriented C++
3.Template C++
4.STL
文中提到的守则,我认为还是从效率层面来讲的。
比如书中的例子:
参数传递中两种方式:值传递和引用传递。当C-风格内置类型参数传递时值传递效率更高,而这个准则到了面向对象的类操作时,由于构造函数和析构函数的存在,引用传递往往更好。
C++高效编程守则视状况而变化,取决于使用C++的哪一部分。
(三)编译器替换预处理器
3.1const代替#define
也就是要减少#define的使用。
原因有二:
1.使用预处理器所用的符号名称可能将未进入记号表,当#define在其他文件时,一旦程序出错,错误信息将难以定位。
2.预处理器可能将宏定义盲目的替换,这也是显而易见的,在我们最早学习C/C++宏定义的时候就知道的简单道理。
取而代之的是用const来代替宏,例如:
#define pi 3.14//替换为
const double pi = 3.14;
使用const替换宏还有两个特殊情况:
一是:
const char * const authorName="Scott Meyers";
要使用两个const限定,表示指向常量的常量指针
,语义上来说是没有问题的。
二是:
class专属的常量,用static修饰,例如:
class GamePlayer{
private:
static const int NumTurns=5;
int scores[NumTurns];
};
带有类内初始值设定项的成员必须是常量也就是const限定字的作用,而static则限定了该常量只有一份实体。这里也侧面反映除了#define的一个小缺陷,因为仅仅是宏名称的替换,因此不能定义class专属的常量,也不能提供封装,即没有所谓的private #define
这种东西。
3.2 enum hack的使用
主要是针对一些编译器不支持类内初始化设定,这样,我们的类内专属变量就必须在内外定义:
class GamePlayer{
private:
static const int NumTurns;
int scores[NumTurns];
};
const int GamePlayer::NumTurns=5;
但是编译器又必须在编译期就知道数组的大小。
这样就会很尴尬,因为这个时候数组scores
的大小就变得未知。
enum hack
就可以解决这个问题,也是因为我们所需要的常量是个整型,而枚举类型是完全可以当做整型来用的。所以,修改上述代码:
class GamePlayer{
private:
enum {NumTurns = 5};
int scores[NumTurns];
};
关于使用enum hack
的三点需要知道的内容:
1.
enum hack
的地址不合法,这一点类似于#define,这就约束了指针和引用的使用。
2.enum hack
是模板元变成的基础技术。
3.enum hack
不会导致非必要的内存分配。
3.3 宏函数的陷阱
书中的例子,特意试了一蛤:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
int f(int a)
{
return a;
}
int main(int argc, char**argv)
{
int a = 5;
int b = 0;
int res = CALL_WITH_MAX(++a, b);//最终a为7
//int res=CALL_WITH_MAX(++a,b+10);//最终a为6
cout << "res=" << res << endl;
cout << "a=" << a << endl;
system("pause");
return 0;
}
遇到这种情况就很尴尬了,CALL_WITH_MAX
的初衷是想把较大的参数作为函数f的实参,却造成了不同情况的不同效果。原因书中没有说明,但仔细一想,由于宏只是字符串的简单替换,上述例子中:
当
a>b
时,++a
会被执行两次(比较一次,比较结果为真时还要执行一次);
而反之a<b
的话,++a
只会在比较时执行一次,因为此时比较结果为假了。
我们使用预处理器实现的宏让它看起来像函数,不会招致函数调用带来的额外开销,但也会出现上述的问题,幸运的是,我们可以用template inline
的方式解决,这都是书中后面将介绍的内容了。
(四)参考
1. Effective C++
2. C++的enum hack