effective C++ 读后感(二) 尽量以const, enum, inline替换 #define
2014-04-01 16:21
477 查看
二、尽量以const, enum, inline替换 #define
#define在C语言中是常见的,用于定义常量。如:#define PI 3.14
但PI会在编译之前就被预处理了,代码中的PI会被3.14替代。所以PI并没有进入记号表内。因此如果代码出现问题的话,错误信息里可能会提到3.14,而没有PI,这会增大查找问题的难度。而且这也会给调试带来一些麻烦。
比如说下面的例子:
#include <iostream> using namespace std; #define PI 3.14 double getCircleArea(double r) { return PI * r * r; } int main() { int r = 1; cout << getCircleArea(r) << endl; return 0; }
例子很简单,就是计算圆面积。定义圆周率为常量。
我们用gdb进行调试,在第10行设置一个断点,进入getCircleArea函数中。如下图:
如果我们想看一下PI倒底是多少,于是输入print PI,结果却查不到:
原因也是PI没有进入记号表中。
说到底,其实就是预处理器在做怪。为了避免这类问题,应该尽量少用或不用预处理器的功能,让编译器替换预处理器。
而如果将PI的定义改成
const double PI = 3.14;将不会出现上面的问题了。
用#define也很容易出错,往往这些错误难以被发现。比如下面的代码本来要计算两个数的乘积:
#include <iostream> using namespace std; #define M 10 #define N M + 100 int main() { cout << M * N << endl; }编译器看到的只是10 * 10 + 100,所以最后结果为200. 如果仍要用#define而得到正确结果,需要将N定义为:
#define N (M + 100)
而使用const就完全不用担心这类问题。
用const代替#define有几点是要注意的
一、关于指针和引用的constconst作用于指针时,有两种方式,一是常量指针,如:
const int *p 或 int const *p表示指针指向的对象为const,但指针还是可以改变指向的地址。
二是指针常量,如:
int * const p
表示指针指向的地址不能变,但指向的对象内容可以变。
这里教大家一个记忆的方法:如果把const翻译成“常量”,把*号翻译成“指针”,那么const int *(或int const *)表类型去掉就是“常量指针”,int *const就是“指针常量”了。而且常量指针顾名思意就是“指向一个常量的指针”,指针常量就是“是一个指针的常量”,这样意思和写法就都记住了。
于是如果我们要定义一个既指向常量,指向地址又不能变的指针那就可以这样:
const char* const name = "warrenfws";
引用也可以加const修饰,但由于引用本身指向的地址是不能改变的,所以只有一种用法:
int b; const int &a = b;
这样能保证a不会修改b的值。这种用法在很多方法传参中经常用到。
二、类的专属常量
常量可以定义在类里面,作为类的专属常量(而#define不可以),如下面的例子:
#include <iostream> using namespace std; class Apple { static int count; const int id; public: Apple(): id(count ++) {} void print() { cout << id << endl; } }; int Apple::count = 0; int main() { Apple apple[5]; for(int i=0; i<5; i++) apple[i].print(); return 0; }
在这个例子中,id中常量,在对象初始化之后被赋值了,之后就不能再修改了。所以const能提供更好的封装性。
但是如果在编译期就需要用到一个类的常量值(比如申明一个大小由一个常量定义的数组),用const就可能行不通了(因为有些编译器不允许在属性定义的时候对其进行申明,也就是说下面的代码在有些编译器下可能不能通过)
class Person { const int nameLen = 10; char name[nameLen]; };
幸运的是,我们可以通过"enum hack"来解决:
class Person { enum { nameLen = 10 }; char name[nameLen]; };
虽然enum更像#define, 我们不能取一个enum的地址。但它相比于#define能提供更好的封装性。
如何解决宏定义
#define还有一个很大的用处就是定义宏。它的缺点在前面提到过,如果合理加上小括号,宏的运行结果很有可能出人意料。而用内联函数或内联的函数模板可以绕过这个问题,并且同时还与宏同样高效。如定义一个取最大值的宏:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
注意小括号。是不是觉得很麻烦?而且太容易出错了。
而用内联函数模板来实现的话:
template<typename T> inline T max(const T &a, const T &b) { return a > b ? a : b; }是不是好理解多了! 类似的还可以在类中定义私有的内联函数,而宏不能。
相关文章推荐
- Effective C++ 条款2 尽量以const,enum,inline替换#define
- 重读经典-《Effective C++》Item2:尽量以const,enum,inline替换#define
- 《Effective C++》Item2:尽量以const,enum,inline替换#define
- Effective C++--条款02:尽量以const,enum,inline替换#define(以编译器替换预处理器)
- Effective C++ 之 Item 2:尽量以 const, enum, inline 替换 #define
- Effective C++ 读书笔记——尽量以const,enum,inline替换 #define
- 重读经典-《Effective C++》Item2:尽量以const,enum,inline替换#define
- Effective C++ 02 尽量以const, enum, inline替换#define 笔记
- Effective C++ -----条款02:尽量以const, enum, inline替换 #define
- 《Effective C++》Item2:尽量以const,enum,inline替换#define
- Effective C++:条款02:尽量以const,enum,inline替换#define
- [Effective C++]条款02 尽量以const,enum,inline替换#define
- 读书笔记《Effective C++》条款02:尽量以const,enum,inline替换#define
- effective C++(第三版)读书笔记一——条款二:尽量以const,enum,inline替换#define
- 《effective c++》条款二:尽量以enum,const,inline替换#define
- Effective C++ 笔记(2):尽量以const,enum,inline替换#define
- 【Effective C++ 读书笔记】条款02: 尽量以 const, enum, inline 替换 #define
- 《Effective C++》学习笔记条款02 尽量以const,enum,inline替换#define
- effective C++ 2_尽量以const,enum,inline替换#define 的学习
- [转]Effective C++ 02 尽量以const, enum, inline替换#define 笔记