在介绍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. java——String的那边破事

    经典的先看下面一段代码,请问最终创建几个对象,分别在哪里? String s0 = new String("luoliang.me"); String s1 = "luo ...

  2. Hadoop学习笔记01——Hadoop分布式文件系统

    Hadoop有一个称为HDFS的分布式系统,全称为Hadoop Distributed Filesystem. HDFS有块(block)的概念,默认为64MB,HDFS上的文件被划分为块大小的多个分 ...

  3. software testing homework2

    一.Checkstyle安装及使用 1.checkstyle插件包:http://sourceforge.net/projects/eclipse-cs/ checkstyle.xml配置文件:htt ...

  4. ngCookies模块

    Angular中ngCookies模块介绍 1.Cookie介绍 Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie.内存Cookie由浏览器维护,保存在 ...

  5. 转: when.js原理和核心实现

    这篇文章可以看作是屈屈同学关于when.js的文章<异步编程:When.js快速上手>的续篇. 屈屈的文章中详细介绍了when.js,在这里关于when.js的使用我就不多复述了,大家可以 ...

  6. Html 小插件6 百度新闻插件

    新闻免费代码"http://news.baidu.com/newscode.html ,便可在输入希望订阅的关键词后,根据相关选项的设置,百度便非常快的在当前页面的文本编辑框内生成相关代码. ...

  7. SPOJ 687 Repeats(后缀数组+ST表)

    [题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i ...

  8. 第三章 用 PowerShell 进行远程管理(remoting)

    第三章 用 PowerShell 进行远程管理(remoting) PowerShell V2 引进了一项强大的新技术,远程(remoting),PowerShell V3 进行了完善和扩展.主要基于 ...

  9. 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...

  10. VIJOS 1512SuperBrother打鼹鼠(二维BIT)

    呵呵.. 二维树状数组,第二维和第一维基本一样. --------------------------------------------------------------------------- ...