文档章节

使用const定义常量的注意点

llwwzz
 llwwzz
发布于 2014/09/04 21:32
字数 1555
阅读 13
收藏 0
条款1:尽量用const和inline而不用#define 
这个条款最好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。这是问题之一。再看下面的语句: 

#define   ASPECT_RATIO   1.653 

编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。 
解决这个问题的方案很简单:不用预处理宏,定义一个常量: 

const   double   ASPECT_RATIO   =   1.653; 

这种方法很有效。但有两个特殊情况要注意。 
首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const: 

const   char   *   const   authorName   =   "Scott   Meyers "; 

关于const的含义和用法,特别是和指针相关联的问题,参见条款21。 

另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员: 

class   GamePlayer   { 
private: 
static   const   int   NUM_TURNS   =   5;   //   constant   eclaration   
int   scores[NUM_TURNS];	 //   use   of   constant 
... 
}; 

还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员: 

const   int   GamePlayer::NUM_TURNS;	//   mandatory   definition; 
//   goes   in   class   impl.file 

你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。 

旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int,   bool,   char   等),还只能是常量。 
在上面的语法不能使用的情况下,可以在定义时赋初值: 

class   EngineeringConstants   {   //   this   goes   in   the   class 
private:	 //   header   file 
static   const   double   FUDGE_FACTOR; 
... 
}; 
//   this   goes   in   the   class   implementation   file 
const   double   EngineeringConstants::FUDGE_FACTOR   =   1.35; 

大多数情况下你只要做这么多。唯一例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义: 

class   GamePlayer   { 
private: 
enum   {   NUM_TURNS   =   5   }	//   "the   enum   hack "   —   makes 
//   NUM_TURNS   a   symbolic   name   
//   for   5 
int   scores[NUM_TURNS];//   fine 
}; 

除非你正在用老的编译器(即写于1995年之前),你不必借用enum。当然,知道有这种方法还是值得的,因为这种可以追溯到很久以前的时代的代码可是不常见的哟。 

回到预处理的话题上来。另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值: 

#define   max(a,b)   ((a)   >   (b)   ?   (a)   :   (b)) 

这个语句有很多缺陷,光想想都让人头疼,甚至比在高峰时间到高速公路去开车还让人痛苦。 
无论什么时候你写了象这样的宏,你必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。但是即使你象这样做了,还会有象下面这样奇怪的事发生: 

int   a   =   5,   b   =   0; 
max(++a,   b);//   a   的值增加了2次 
max(++a,   b+10);   //   a   的值只增加了1次 

这种情况下,max内部发生些什么取决于它比较的是什么值! 
幸运的是你不必再忍受这样愚笨的语句了。你可以用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数(见条款33): 

inline   int   max(int   a,   int   b)   {   return   a   >   b   ?   a   :   b;   } 
不过这和上面的宏不大一样,因为这个版本的max只能处理int类型。但模板可以很轻巧地解决这个问题: 

template <class   T> 
inline   const   T&   max(const   T&   a,   const   T&   b) 
{   return   a   >   b   ?   a   :   b;   } 

这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。因为不知道T的类型,返回时传递引用可以提高效率(见条款22)。 

顺便说一句,在你打算用模板写象max这样有用的通用函数时,先检查一下标准库(见条款49),看看他们是不是已经存在。比如说上面说的max,你会惊喜地发现你可以后人乘凉:max是C++标准库的一部分。 
有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。预处理还不能退休,但你一定要计划给它经常放长假。


本文转载自:http://zhidao.baidu.com/link?url=Ulu6A_KqWIVLF3_3njeoycrKpei41BN9rFj3j3hvUxPlf28nWuip5PS7CnSDjehS...

上一篇: 快速排序
llwwzz
粉丝 1
博文 25
码字总数 15437
作品 0
敦煌
私信 提问
C#基础(201)--常量枚举

  本文知识点:     1.掌握常量的定义和使用方法     2.理解枚举的作用和特点     3.掌握枚举的使用方法 1.1.常量的定义语法   const 数据类型 常量名称 = 值; 1.2.常见错误...

梓鹏
03/01
0
0
iOS正确使用const,static,extern

static 修饰局部变量 让局部变量只初始化一次 局部变量在程序中只有一份内存 并不会改变局部变量的作用域,仅仅是改变了局部变量的生命周期(只到程序结束,这个局部变量才会销毁) 修饰全局...

Daniel_s
2016/11/21
16
0
iOS 不要用宏来定义你的常量

首先,预处理命令他不是一个常量!!!! 我们来看一段代码 这段代码会输出多少,我们将“avatar”定义为了60,然后在一个永远不会执行的代码里面重新定义了“avatar”为80,if语句中的代码 ...

啊子同
2016/07/21
61
0
【如何正确使用const,static,extern】|那些人追的干货

【如何正确使用const,static,extern】 那些人追的干货 一、const与宏的区别(面试题): :之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量。 :宏是预编...

Gong_xiao
2015/10/23
30
0
var ,let ,const 的区别和共同点

一、let和var区别 1.关于变量提升,var能变量提升,let不能 2.暂时性死区:块级作用域内存在let命令,它所声明的变量就“绑定”这个区域,不再受外部的影响重点内容,简而言之,就是某个代码块...

MrBoyce
04/26
16
0

没有更多内容

加载失败,请刷新页面

加载更多

为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
18分钟前
2
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
29分钟前
2
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
30分钟前
2
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
36分钟前
6
0
IDEA 自定义方法注解模板

IDEA 自定义方法注解模板 1、使用效果 /*** 计算交易费用* @Author wangjiafang* @Date 2019/9/11* @param feeComputeVo* @return*/@PostMapping("/v1/fee_compute")public ApiResp......

小白的成长
36分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部