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

for (int i = 0; i < 512; ++i) {
…;
}

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

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

#define MAX_NUM_SIZE 512

for (i = 0; i < MAX_NUM_SIZE; ++i) {
…;
}

通过使用#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,可进行如下定义:

const int MAX_NUM_SIZE = 512;

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

class GamePlayer
{
private:
static const int MAX_NUM_SIZE; // static修饰符是为了确保此常量只有一份实体
}; const int GamePlayer:: MAX_NUM_SIZE = 512;

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

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

const int MAX_NAME_LENGTH = 128;
const float LOG_2E = log2(2.71828183f);
const std::string LOG_FILE_NAME = “filename.log”;

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

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

// myconst.h
extern const int MAX_NAME_LENGTH;
extern const float LOG_2E;
extern const std::string LOG_FILE_NAME;

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

// myconst.cpp
const int MAX_NAME_LENGTH = 128;
const float LOG_2E = log2(2.71828183f);
const std::string LOG_FILE_NAME = “filename.log”;

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

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

// myclass.h
class MyClass
{
public:
static const int MAX_NAME_LENGTH;
static const float LOG_2E;
static const std::string LOG_FILE_NAME;
};

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

// myclass.cpp
const int MAX_NAME_LENGTH = 128;
const float LOG_2E = log2(2.71828183f);
const std::string LOG_FILE_NAME = “filename.log”;

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

参考资料:

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

2. C++ API 设计

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

C++使用之常量的定义的更多相关文章

  1. PHP 7: PHP 变量和常量的定义

    原文:PHP 7: PHP 变量和常量的定义 本章说说变量的定义.如果对于变量和常量的定义,你会注意几个方面呢?你可能会想到: 如何定义变量,它和C# 等语言有什么不同呢? 变量区分大小写吗? PHP ...

  2. php课程 2-7 php中常量如何定义

    php课程 2-7 php中常量如何定义 一.总结 一句话总结:函数方式定义和普通变量方式定义    define('PI','3.14').键值对,和session一样.    const PI=3 ...

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

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

  4. PHP内置常量,和可变变量,常量的定义

    关键常量 可变变量----变量名是变量的变量 常量的定义

  5. C基础知识(2):变量&常量的定义和声明

    变量定义和声明 (1) 使用int,char等类型符定义变量 使用int,char等类型符定义变量时,不管有没有指定初始值,都就已经建立了存储空间(开辟内存).内存寻址由大到小,优先分配内存地址比较大 ...

  6. thinkphp __PUBLIC__的定义 __ROOT__等常量的定义

    2 3 4 5 6 7 8 9 '__TMPL__'      =>  APP_TMPL_PATH,  // 项目模板目录 '__ROOT__'      =>  __ROOT__,    ...

  7. JAVA final关键字,常量的定义

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

  8. PHP扩展开发(5) - PHP常量的定义和读取

    1. 定义   //定义PHP常量REGISTER_STRINGL_CONSTANT("SIMPLE_VERSION", PHP_SIMPLE_VERSION, sizeof(PH ...

  9. define 常量的定义和读取

    define(‘常量’,‘常量值’)----------------------define来定义常量, echo 也能输出常量, get_defined_constants(true)------- ...

随机推荐

  1. leetcode two sum python

    class Solution: # @param {integer[]} nums # @param {integer} target # @return {integer[]} def twoSum ...

  2. 在Java SE中使用Hibernate处理数据

    如今,Hibernate正在迅速成为非常流行的(如果不是最流行的)J2EE O/R映射程序/数据集成框架.它为开发人员提供了处理企业中的关系数据库的整洁.简明且强大的工具.但如果外部需要访问这些已被包 ...

  3. XSS【跨站脚本攻击】

    从客户端(txt="<script><a href="www...")中检测到有潜在危险的 Request.Form 值. 如果你使用的是.NET 3. ...

  4. hdu 4630 No Pain No Game 线段树离线处理

    题目链接 求出一个区间内任意两个数的gcd的最大值. 先将询问读进来然后按r值排序. 将每一个数因数分解, 对每一个因子x, 如果pre[x]!=-1, 那么就更新update(pre[x], x, ...

  5. php基础知识总结

    PHP 代表 PHP: Hypertext Preprocessor PHP 文件可包含文本.HTML.JavaScript代码和 PHP 代码 PHP 代码在服务器上执行,结果以纯 HTML 形式返 ...

  6. U盘中的闪存白片与黑片

    简单的说,黑片的概念主要用于芯片,白片的概念既用于芯片也用于闪存卡.黑片就是指芯片工厂选出的淘汰的次品,没有打上工厂标和芯片型号的芯片,这样的芯片都经过个种渠道流通到市场上来,现在很多U盘大厂大量的采 ...

  7. Android学习路径图

    一个PHPer转战Android学习过程: 直接跨过java的学习,原因有我之前看过毕向东和张孝祥的Java基础课程,虽然中间好几次看睡着,但java的环境是能跑起来的.我建议大家如果没有Java基础 ...

  8. Codeblock解决注释乱码问题及在ubuntu中程序运行时乱码问题。

    (1)修改源文件保存编码在:settings->Editor->gernal settings>other settings 看到左边的Encoding group Box,改为WI ...

  9. openStack 王者归来之 trivial matters

    <一,openStack img 制作> tips:制作大部分cloud platforms img准备工作. <1,> http://www.pubyun.com/blog/ ...

  10. xcode 不值钱的动画UIButton

    #import "ViewController.h" @interface ViewController () /** 按钮 */ @property(nonatomic,weak ...