宁可使用编译器而不用预处理器

  假设我们使用预处理器:

#define ABC 1.56

  这标识符ABC也许编译器没看到,也许它在编译器处理源码前就被预处理器移走了,于是“标识符”ABC没有进入标识符列表(symbol table)中。但是当我们编译程序遇到个错误信息时,可能会带来困惑,因为这个错误信息可能会提到1.56而不是ABC,而下面例子在vs2015上编译的时候,错误信息提示ABC未定义的标识符。假如这个ABC不在我们自己定义的头文件中,我们根本无法知道其来源,追踪不到它。
解决之道就是使用一个常量代替宏定义:

const double abc=1.56;

  这样的话abc就是一个标识符,肯定会被编译器看到,必然会进入标识符列表。

 #include<iostream>
using namespace std; #define ABC 3
const int a = ; int func(const int* n)
{
return *(n + );
} int main(void)
{
int x,y;
x = func(&ABC);//vs2015会提示表达式必须为左值或者标识符
cout << x << endl; y = func(&a);
cout << y << endl;
return ;
}

常量定义特殊情况说明:
  (1).由于常量定义被放在头文件中,指针声明为const,假如是char*字符串的话,const就要写两次。

const char* const str="Burgess";

  string对象往往写成:

const std::string("Burgess");

  (2).class专属常量。为了将作用域限定在class内,必须使它成为一个成员变量。为了保证这个常量只有一个实体,要将它声明为static。

 class A
{
private:
static const int Num=;//常量声明式
int scores[Num];//使用该常量
};

  一般情况下,我们还需要在class外进行定义它。

const int A::Num;

  由于已经在类里面设了初值,这里不必再初始化。(旧编译器不支持在类里面为static const常量设初值),那么就要:

 class A
{
private:
static const int Num;//声明式
};
const int A::Num=;//定义式

这个定义式放在实现文件而不是头文件中。
需要说明的是,类里面的函数使用这个变量时,也需要声明为static。
假如上面的数组大小坚持使用一个标识符来代替怎么办呢,因为编译器必须在编译期间知道数组大小,那么我们就可以使用一个枚举类型的数值代替int型数值。

 class A
{
private:
enum{Num=};
int scores[Num];
};

这里enum类似#define,不能取地址,只需要获取其值。假如我们不想让别人获取指向某个常量的指针或者引用,可以使用enum。
让我们再返回宏定义。举个误用宏定义的例子。有时我们使用#define实现类似函数的宏定义,但是没有函数调用引来的额外开销。看下面:

 #define CALL_MAX(a,b) ((a)>(b))?(a):(b)
int main(void)
{
int ret1,ret2,a = , b = ;
ret1 = CALL_MAX(++a, b);//现在a=7,不可思议
cout<< ret1<<endl; ret2 = CALL_MAX(++a, b+);//a=6
cout << ret2 << endl;
return ;
}

为什么 CALL_MAX(++a, b+10),此时a=6?
把参数代入表达式就可以知道:

((++a)>(b+))?(++a):(b+)

可以看成6>10吗,显然不是。那么就返回b+10=10,而不再执行++a,故a也不会再加1了。

如果不想要这种未知行为的#define,但是还想要宏定义的效率、安全性和可预知性(一切掌握在我们自己手里),那么inline模板函数是个好选择:

 Template <typename T>
inline T const& max1(const &T a,const &T b)
{
return (a>b) ? a : b;
}

  这样的话,不会出现上面#define出现的++a加1进行了两次的情况,此时max1是真正的函数。

请记住:
  (1).单纯的常量,最好使用const或者enum代替#define;
  (2).类似函数的宏,最好以inline函数代替#define。

条款2:尽量使用const ,enum,inline替换define的更多相关文章

  1. 读书笔记_Effective_C++_条款二:尽量以const, enum, inline替换#define

    其实这个条款分成两部分介绍会比较好,第一部分是用const和enum替换不带参的宏,第二部分是用inline替换带参的宏. 第一部分:用const和enum替换不带参宏 宏定义#define发生在预编 ...

  2. Effective C++阅读笔记_条款2:尽量以const,enum,inline替换#define

    1.#define缺点1 #define NUM 1.2 记号NUM可能没有进入记号表,在调试或者错误信息中,无法知道1.2的含义. 改善:通过const int NUM = 1.2; 2.#dein ...

  3. Effective C++学习笔记 条款02:尽量以const,enum,inline替换 #define

    尽量使用const替换 #define定义常量的原因: #define 不被视为语言的一部分 宏定义的常量,预处理器只是盲目的将宏名称替换为其的常量值,导致目标码中出现多分对应的常量,而const定义 ...

  4. 条款2:尽量以const, enum, inline替换#define

    原因: 1. 追踪困难,由于在编译期已经替换,在记号表中没有. 2. 由于编译期多处替换,可能导致目标代码体积稍大. 3. define没有作用域,如在类中定义一个常量不行. 做法: 可以用const ...

  5. 条款02:尽量以const,enum,inline替换#define

    目录 1. 总结 2. 使用const常量或enum替换宏常量 class外部的常量指针 class专属常量 1. 总结 对于单纯常量,最好以const常量或enum替换#define 对于宏代码段, ...

  6. NO.2: 尽量以const,enum,inline 替换 #define

    1.首先#define 定义不重视作用域(scope),虽然可以#undef控制,但是不美观,还存在多次替换的问题,以及没有任何封装性. 2.const XXX_XX,保证其常量性以及可控的作用域,如 ...

  7. Effective C++ -----条款02:尽量以const, enum, inline替换 #define

    class GamePlayer{private: static const int NumTurns = 5; int scores[NumTurns]; ...}; 万一你的编译器(错误地)不允许 ...

  8. Effective C++之条款2:尽量以const enum inline替换 #define

    本文的标题也可以改成“用编译器替换预处理器”: const double AspectRatio = 1.653; //最好使用上述代码替换下述代码: #define ASPECT_RATIO 1.6 ...

  9. Book. Effective C++ item2-尽量使用const, enum, inline替换#define

    ##常规变量 c++里面的#define后面的定义部分,是不算代码的一部分的.所以如果你使用#define: #define ASPECT_RATIO 1.653 你希望这个代号ASPECT RATI ...

随机推荐

  1. Windows 下搭建 SVN服务器

    目录 一 .安装Visual SVN 二.配置SVN 三.安装TortoiseSVN 四.上传项目到远程仓库 五.从远程仓库下载项目 六.检出项目 七.版本回退   参考链接 https://blog ...

  2. Nginx-配置负载均衡实例

    配置负载均衡实例 实现效果: 配置负载均衡 实验代码 1) 首先准 备两个同时启动的 Tomcat 2) 在 nginx.conf 中进行配置 随着互联网信息的爆炸性增长,负载均衡(load bala ...

  3. shell学习记录----初识sed和gawk

    Linux命令行与shell脚本编程大全中关于sed和gawk的介绍合在一起,而且结构有点乱. 不像之前的命令写的很清楚.所以这次我需要写下来整理一下. 一.sed部分 1.1 sed命令格式如下: ...

  4. UEditor可以如何直接复制word的图文内容到编辑器中

    如何做到 ueditor批量上传word图片? 1.前端引用代码 <!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN& ...

  5. python cv2在验证码识别中的使用

    使用函数cv2.imread(filepath,flags)读入一副图片 filepath:要读入图片的完整路径 flags:读入图片的标志 cv2.IMREAD_COLOR:默认参数,读入一副彩色图 ...

  6. openlayers筛选图层

    很多时候需要筛选图层,例如选择交互(ol.interaction.Select). 图片来自官方:https://openlayers.org/en/v4.6.5/apidoc/ol.interact ...

  7. Bulk synchronous parallel

    https://en.wikipedia.org/wiki/Bulk_synchronous_parallel https://zh.wikipedia.org/wiki/整体同步计算模型 超级步(S ...

  8. MySQL case when then else end用法

    链接:https://blog.csdn.net/konglongaa/article/details/80250253 case具有两种格式,简单case函数和case搜索函数. 1.简单case函 ...

  9. 121、TensorFlow张量命名

    # tf.Graph对象定义了一个命名空间对于它自身包含的tf.Operation对象 # TensorFlow自动选择一个独一无二的名字,对于数据流图中的每一个操作 # 但是给操作添加一个描述性的名 ...

  10. Oracle-创建索引分区

    对大数据量索引进行分区同样能够优化应用系统的性能.一般来说,如果索引所对应的表的数据量非常大,比如几百万甚至上千万条数据,则索引也会占用很大的空间,这时,建议对索引进行分区. Oracle索引分区分为 ...