在介绍C++的常量前,先看下下面的代码。

[cpp] view plaincopy
  1. for (int i = 0; i < 512; ++i) {
  2. …;
  3. }

512是什么,它具有什么含义?在代码中若直接使用类似512这些“魔数”(magic number),这些数字的具体含义就不能体现出来。另外,假如程序中多处包含512这个数,随着时间的推移,发现有些512需要更改为1024,有些512需要保持不变。这种情况下,程序员必须小心谨慎地去一个一个地查找程序中所有的512,并保证更改的数字不会出错——使用“魔数”会导致难以维护的问题。

为了解决以上问题,可以使用#define预处理器指令来定义一个常量。例如,

[cpp] view plaincopy
  1. #define MAX_NUM_SIZE 512
  2. for (i = 0; i < MAX_NUM_SIZE; ++i) {
  3. …;
  4. }

通过使用#define定义MAX_NUM_SIZE这个宏,可以清晰看到512的具体含义,另外,假如需要更改这个数字,只须直接修改#define一处就可以了。

然而,在C++中,并不提倡使用#define定义一个常量。#define本质上是一个预处理器指令,它仅仅表示使用一个串代替别一个串而已。也就是说,#define定义的常量从未被编译器看到——它们在编译器开始处理源码之前就被移走了。

具体来说,避免使用#define来定义常量,原因如下所述。

1. 没有指定类型

#define不涉及为定义的常量做类型检查,为了显式地指定常量类型,需要在常量后加上后缀。比如,对于float类型的常量,在数字后面加上f后缀。

2. 没有指定作用域

#define定义的常量是全局的。

3. 没有访问控制

不能把#define定义的常量标记为公有的,受保护的,或者私有的,它本质上是公有的。

因为宏一旦被定义,它就在其后的编译过程中有效(除非在某处被#undef)。

4. 没有符号

前面的例子中,宏MAX_NUM_SIZE可能会被预处理器从代码中剥离,这样,编译器就无法看见这个名字。这样,程序员在调试时只能看到一些没有任何描述性的常量值。

与使用#define定义常量相比,更可取的办法是使用C++的const限定修饰符来定义常量。例如,对于MAX_NUM_SIZE,可进行如下定义:

[cpp] view plaincopy
  1. const int MAX_NUM_SIZE = 512;

作为一个整型常量,const修饰的MAX_NUM_SIZE肯定被编译器看到,当然会进入符号表。假如将MAX_NUM_SIZE定义在某个类中,还可以对其实施访问控制器和为其指定作用域。例如:

[cpp] view plaincopy
  1. class GamePlayer
  2. {
  3. private:
  4. static const int MAX_NUM_SIZE;  // static修饰符是为了确保此常量只有一份实体
  5. };
  6. const int GamePlayer:: MAX_NUM_SIZE = 512;

在上述的代码中,我们定义了一个int型的常量,该常量的作用域为GamePlayer类在,并标记为私有访问,并且会进入符号表。

值得指出的是,要注意const定义常量在某种程度上可能会使代码更臃肿。例如,在某个头文件中定义的了以下的一些常量:

[cpp] view plaincopy
  1. const int MAX_NAME_LENGTH = 128;
  2. const float LOG_2E = log2(2.71828183f);
  3. const std::string LOG_FILE_NAME = “filename.log”;

在默认情况下,以这种方式定义的变量会促使编译器为每个包含此头文件的模块分配变量存储空间。如果定义了很多常量,并且该头文件被很多.cpp文件包含,那么会导致.o目标文件和最终的二进制文件膨胀。

解决办法是在头文件中使用extern声明常量:

[cpp] view plaincopy
  1. // myconst.h
  2. extern const int MAX_NAME_LENGTH;
  3. extern const float LOG_2E;
  4. extern const std::string LOG_FILE_NAME;

然后在相应的.cpp文件中定义每个常量的值:

[cpp] view plaincopy
  1. // myconst.cpp
  2. const int MAX_NAME_LENGTH = 128;
  3. const float LOG_2E = log2(2.71828183f);
  4. const std::string LOG_FILE_NAME = “filename.log”;

通过这种方式,变量的空间就只会分配一次。

使用extern来声明的常量是全局的,若要将常量的作用域限制在类中,则须在类中声明常量,并将其声明为static(这样它们就不会计入每个对象的内存大小中)。

[cpp] view plaincopy
  1. // myclass.h
  2. class MyClass
  3. {
  4. public:
  5. static const int MAX_NAME_LENGTH;
  6. static const float LOG_2E;
  7. static const std::string LOG_FILE_NAME;
  8. };

然后在相应的.cpp文件中定义这些常量:

[cpp] view plaincopy
  1. // myclass.cpp
  2. const int MAX_NAME_LENGTH = 128;
  3. const float LOG_2E = log2(2.71828183f);
  4. const std::string LOG_FILE_NAME = “filename.log”;

当然,在某些情况下,使用枚举类型代替常量也可以避免文件空间膨胀的问题。

参考资料:

1. C++ Primer 中文版,第三版

2. C++ API 设计

3. Effective C++ 中文版,第三版

C++使用之常量的定义相关推荐

  1. 常量的定义与使用 1006 c#

    常量的定义与使用 1006 为什么需要常量 程序中,固定不变的数,就是常量 例如: 一周有七天,无论程序进行到哪个步骤 七天,这个数据都是不会变的 七这个数据,就适合使用常量保存 常量的特点 如果一个 ...

  2. C/C++ 常量的定义与应用(编程中的常量)

    常量一般定义为全局变量,且大写: 1. 字符串常量 const string EXPAND_X = "X+YF"; const string EXPAND_Y = "FX ...

  3. 关于经典面试一年多少秒的思考!启发#define与UL!整形常量的定义

    关于经典面试一年多少秒的思考!启发#define与UL! 2016年01月11日 13:52:03 Agou_66 阅读数:1935 标签: C语言#defineUL宏定义一年多少秒 更多 个人分类: ...

  4. jquery 定义php变量,php定义常量_php 定义常量define与普通变量

    摘要 腾兴网为您分享:php 定义常量define与普通变量,中信期货,中国体育,中国搜索,小米钱包等软件知识,以及ps人像插件,河南电大在线,自拍神器手机,国金证券,ipa包,微商加人软件,山东省地 ...

  5. HART协议通用结构体及地址、常量的定义

    HART协议通用结构体及地址.常量的定义 在HART模块中,通常是通过UART发送一个字节,且需要11位二进制数据,第一位起始位,第二到九位共八位是要发送的数据,第十位是校验位<奇校验>, ...

  6. python怎么定义常量_Python定义常量

    阅读目录 一.Python定义常量 Python定义常量 constant.py 定义常量类 import sys class _const: # 自定义异常处理 class ConstError(P ...

  7. C语言(三)常量的定义

    常量的定义 常量的定义 #include<stdio.h> #define 四大皆空 main //void main1() //{ // const int x = 10;//常量,co ...

  8. java项目中常量规范定义的思考

    大言不惭,则无必为之志.--<论语·宪问> 1.引言 最近在看老大在项目中写的代码,发现在系统常量的定义上,与我之前在开发项目的定义有些不一样,引发了我对系统变量如何规范定义和每一种定义有 ...

  9. Java final关键字,常量的定义

    final(最终)是一个修饰符 1.final可以修饰类,函数,变量(成员变量,局部变量) 2.被final修饰后的类不可以被其它类继承 3.被final修饰后的方法(函数)不可以被重写 4.被fin ...

  10. ASP语言基础之常量的定义方法

    ASP 定义常量的方法 常量:用一种名称代替数字和字符串,且其值保持一直不变. 在 VBscript 中,可以用 const 语句来定义常量. 常量分数字型和字符串型. 表示日期的常量写在两个 # 之 ...

最新文章

  1. 使用jQuery和YQL,以Ajax方式加载外部内容
  2. php中extends是什么意思,在php中extends与implements的区别
  3. Spring AOP 面向切面编程
  4. 这些Python骚操作,你值得拥有
  5. 【java基础知识】spring框架开发时,怎样解决mysql数据库中Timestamp到String的简单转换
  6. chrome保护眼睛设置【转】
  7. 失败的面试小记,项目面,酷家乐面筋
  8. 程序员自我修改之读书学习
  9. Android环境搭建
  10. Cocos2d-x 学习笔记(11.1) MoveBy MoveTo
  11. XUPT第三届新生算法赛
  12. mongodb基本命令及操作
  13. html怎么修改表格行列间距,html表格如何设置间距
  14. 国家信息分类和编码标准
  15. windows 电脑锁屏时,WPS软件自带屏保图片保存的位置
  16. Castor学习笔记(一)
  17. 你所羡慕的生活背后,都是苦行僧般的自律
  18. 没有找到libgcc_s_sjlj-1.dll
  19. MySQL高可用方案之PXC架构
  20. Vue移动端——隐藏滚动条

热门文章

  1. Tribon二次开发-第三方程序操作Tribon数据
  2. 学社圆桌之EMC综合训战
  3. Python中列表常用方法总结
  4. 什么是测试界天花板,我今天算是见到了
  5. 如何正确学习JavaScript
  6. Linux 磁带机备份完全攻略
  7. iOS应用内支付(IAP)的那些坑
  8. python知识图谱问答系统代码_知识图谱和问答系统
  9. 性能测试必备知识(7)- 深入理解“CPU 使用率”
  10. 关于测试的起始 : 测试系统工程师TSE的职责与培养