《Effective C++》读书笔记 条款02 尽量以const,enum,inline替换#define
Effective C++在此条款中总结出两个结论
1.对于单纯常量,最好以const对象或enum替换#define
2.对于形似函数的宏,最好改用inline函数替换#define
接下来我们进行详细的探讨。
const替换#define的讨论
例如:
#define ASPECT_RATIO 1.653
原书给出的解释大意是:
你所使用的名称(ASPECT_RATIO)可能并未进入记号表(symbol table),原因也许是记号名称ASPECT_RATIO从未被编译器看见,也许在编译器开始处理源码之前他就被预处理器移走了。从而导致一个后果:当你运用此常量但获得一个编译错误信息时,这个信息也许会提到1.653而不是ASPECT_RATIO(因为所有的ASPECT_RATIO都被1.653代替),使得debug出现困难,因为你可能对1.653来自何处毫无概念,特别在多个文件都定义了ASPECT_RATIO时,无法确定是哪一个常量出了问题。
记号表:每个可重定位目标模块都有一个符号表,它包含m所定义和引用的符号的信息,在链接器的上下文中,有三种不同的符号: 1.由m定义并能被其他模块引用的全局符号 2.由其他模块定义并被模块m引用的全局符号 3.只被模块m定义和引用的本地符号
可重定位目标文件: 包含二进制代码和数据,其形式可以在编译时与其他可重定义目标文件合并起来,创建一个可执行目标文件。
解决之道是以一个常量替换上述的宏:
const double AspectRatio = 1.653;
作为一个语言常量,AspectRatio肯定会被编译器看到,当然会进入记号表内。此外对浮点常量而言,使用常量可能比使用#define导致较小量的码,因为预处理器盲目将宏名称替换为1.653,可能导致目标码(object code)出现多份1.653,若改用常量绝不会出现相同情况。
以常量替换#define,有两种特殊情况值得注意:
1.定义常量指针。若要在头文件内定义一个指向常量的常量指针,需要些const两次
const char* const pc = "c++";//指向常量字符串的常量char*型指针
一个小知识点是,string对象比char*类型的指针更合意,所以pc定义成下面的样式更好些
const std::string pc("c++");//常量string对象
2.class类内的专属常量,为保证至多只有一个实体,必须声明为static。
class GamePalyer { private: static const int NumTurns = 5;//常量声明式 int scores[NumTurns];//使用该常量 }
对于static类型的class专属常量,若它是整数类(例如int,char,bool),只要不取他们的地址,可以声明并使用它们而无须提供定义式。
如果必须用到定义式,必须在实现文件而不是头文件如下定义
const int GamePlayer::NumTurns;//NumTurns的定义
由于NumTurns已在声明时获得初值,因此定义时不可以再设初值。如果你的编译器不支持static成员在其声明式上获得初值,可以将初值放在定义式。
static const int NumTurns;//声明 const int GamePalyer::NumTurns = 5;//定义
以enum替换#define的讨论
当你在class编译期间需要一个class常量值,如果你的编译器不允许static整数型class常量完成类内初值设定,可使用enum。
class GamePlayer { private: enum { NumTurns = 5 }; int scores[NumTurns]; }
这样做的好处是enum不允许取地址,有时这正是我们想要的。如果不想让别人获得一个pointer或reference指向你的整数常量,enum可以帮助实现这个约束。
inline替换#define的讨论
#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被累加一次
这时可以使用template inline函数避免这些缺陷
template<tyoename T> inline void callWithMax(cosnt T& a,cosnt T& b) { f(a > b ? a : b); }
《Effective C++》读书笔记 条款02 尽量以const,enum,inline替换#define的更多相关文章
- Effective C++学习笔记 条款02:尽量以const,enum,inline替换 #define
尽量使用const替换 #define定义常量的原因: #define 不被视为语言的一部分 宏定义的常量,预处理器只是盲目的将宏名称替换为其的常量值,导致目标码中出现多分对应的常量,而const定义 ...
- 读书笔记_Effective_C++_条款二:尽量以const, enum, inline替换#define
其实这个条款分成两部分介绍会比较好,第一部分是用const和enum替换不带参的宏,第二部分是用inline替换带参的宏. 第一部分:用const和enum替换不带参宏 宏定义#define发生在预编 ...
- NO.2: 尽量以const,enum,inline 替换 #define
1.首先#define 定义不重视作用域(scope),虽然可以#undef控制,但是不美观,还存在多次替换的问题,以及没有任何封装性. 2.const XXX_XX,保证其常量性以及可控的作用域,如 ...
- Effective C++阅读笔记_条款2:尽量以const,enum,inline替换#define
1.#define缺点1 #define NUM 1.2 记号NUM可能没有进入记号表,在调试或者错误信息中,无法知道1.2的含义. 改善:通过const int NUM = 1.2; 2.#dein ...
- Effective C++ -----条款02:尽量以const, enum, inline替换 #define
class GamePlayer{private: static const int NumTurns = 5; int scores[NumTurns]; ...}; 万一你的编译器(错误地)不允许 ...
- Effective C++之条款2:尽量以const enum inline替换 #define
本文的标题也可以改成“用编译器替换预处理器”: const double AspectRatio = 1.653; //最好使用上述代码替换下述代码: #define ASPECT_RATIO 1.6 ...
- 条款02:尽量以const,enum,inline替换#define
目录 1. 总结 2. 使用const常量或enum替换宏常量 class外部的常量指针 class专属常量 1. 总结 对于单纯常量,最好以const常量或enum替换#define 对于宏代码段, ...
- 条款2:尽量以const, enum, inline替换#define
原因: 1. 追踪困难,由于在编译期已经替换,在记号表中没有. 2. 由于编译期多处替换,可能导致目标代码体积稍大. 3. define没有作用域,如在类中定义一个常量不行. 做法: 可以用const ...
- 条款2:尽量使用const ,enum,inline替换define
宁可使用编译器而不用预处理器 假设我们使用预处理器: #define ABC 1.56 这标识符ABC也许编译器没看到,也许它在编译器处理源码前就被预处理器移走了,于是“标识符”ABC没有进入标识符列 ...
随机推荐
- vue+webpack搭建项目
1.全局安装node.js 2.安装vue-cli 可以在项目目录安装 npm install -g vue-cli 使用vue-list命令选择webpack模板 vue init webpack ...
- oracle 12如何解锁账户锁定状态及修改忘记的密码
有两种方法,大同小异吧,感觉命令真是个好东西,哈哈哈哈,挽救了我安了4次才安好的oracle!!! 方法一: 1.如果忘记密码,找到忘记密码的是哪个用户身份,如果用户被锁定,可以使用下面说的方法解除锁 ...
- [videos系列]日本的videos视频让男人产生了哪些误解?
转载自:[videos系列]日本的videos视频让男人产生了哪些误解? 日本的videos视频是每个男人成长过程中都会看的启蒙教育片,也是男人在成年后调剂生活的必需品,但是由于影视作品是艺术的,是属 ...
- JavaScript document对象
1.document对象是window对象的子对象,可直接使用,多用于获取HTML页面元素 2.document对象属性 a) alinkColor活动链接颜色 b) linkColor文本链接颜色 ...
- 在vue-cli + webpack 项目中使用sass
1.准备工作: 由于npm的服务器在国外,网速慢而且安装容易失败,建议在安装之前,先安装国内的镜像,比如淘宝镜像 npm install -g cnpm --registry=https://regi ...
- 创建在类路径资源[applicationcontext]中定义名为“工厂”的bean时出错。:在设置bean属性“dataSource”时,无法解析对bean“dataSource”的引用;嵌套异常是org.springframe .beans.factory。BeanCreationException:创建名为“数据源”的bean时出错,该名称是在类路径资源[applicationcontext
控制台报错: 创建在类路径资源[applicationcontext]中定义名为“工厂”的bean时出错.:在设置bean属性“dataSource”时,无法解析对bean“dataSource”的引 ...
- Django中间件执行顺序
中间件 Django中的中间件是一个轻量级.底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出.中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django ...
- 导航栏NavigationBar的按钮设置
有时候在自定义navigationBar的左右按钮的时候,button的图片会显得很大,个人感觉原因有以下几种情况: 1.使用的是UIButton直接加在navigationBar上面 2.自定义了一 ...
- java读写HDFS
package cn.test.hdfs; import java.io.IOException; import java.net.URI; import java.net.URISyntaxEx ...
- Arduino UNO仿真开发环境设置和仿真运行
一. Proteus仿真平台简介 Proteus软件是英国Labcenter electronics公司出版的EDA工具软件(该软件中国总代理为广州风标电子技术有限公司).它不仅具有其它EDA工具软件 ...