C++ const 数据存储位置
看如下代码:
class GamePlayer { private: static const int NumTurns = 5; // 常量声明式 int scores[NumTurns]; // 使用该常量 ... }
然而你所看到的是 NumTurns 的声明式而非定义式。通常 C++ 要求你对你所使用的任何东西提供一个定义式, 但如果它是一个 class 专属常量又是 static 且为整数类型(intergral type,例如 ints,chars,bools),则需特殊处理。只要不取他们的地址, 你可以声明并使用它们而无须提供定义式。但如果你取某个 class 专属常量的地址,或纵使你不取其地址而你的编译器却(不正确地)坚持要看到一个定义式, 你就必须另外提供定义式如下:
const int GamePlayer::NumTurns; // NumTurns 的定义; // 下面告诉你为什么给予数值
请把这个式子放进一个实现文件而非头文件。由于 class 常量已在声明时获得初值,因此定义时不可以再设初值。
以上所有来自《Effective C++》第三版 P14
我不理解的是 不取其地址的时候,可以访问 NumTurns ? ,于是我代码进行验证:
#include <iostream> class GamePlayer { public: static const int NumTurns = 5; // 常量声明式 }; const int GamePlayer::NumTurns; int main() { std::cout << GamePlayer::NumTurns << std::endl; // 如果没有加上 const int GamePlayer::NumTurns; 对 NumTurns 进行定义,下面这样语句会报错: // undefined reference to `GamePlayer::NumTurns std::cout << &(GamePlayer::NumTurns) << std::endl; return 0; }
代码的验证结果和书中说的是一致的。那么问题就来了,NumTurns 没有定义的时候可以访问,却无地址,那它存储在什么地方呢?
- 调试的时候会发现
GamePlayer::NumTurns
没有定义的时候,Watch 窗口找不到GamePlayer::NumTurn
s 这个符号,而有定义了之后, Watch 窗口可以找到GamePlayer::NumTurns
这个符号并且值为 5。 - 对两次的代码进行反汇编,发现他们的反汇编代码是完全相同的。
于是我猜测,难道未定义时,编译器会把它当成宏来处理?只是进行了符号替换操作(我指的是狭义的工作方式,并不是指在预处理时候进行的替换), 要不然怎么会没有地址呢?我网上查的时候看到一个CSDN的帖子内容挺好的。我将感觉能解释的通的几个点罗列了一下:
- const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像
#define
一样给出的是立即数, 所以,const 定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 - 编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作, 使得它的效率也很高。
- C++ 中的 const 默认为内部连接,也就是说,const 仅在 const 被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。 当定义一个 const 时,必须赋一个值给它,除非用 extern 作出了清楚的说明。
- 通常 C++ 编译器并不为 const 创建存储空间,相反它把这个定义保存在它的符号表里。但是 extern 强制进行了存储空间分配 (另外还有一些情况,如取一个 const 的地址,也要进行存储空间分配),由于 extern 意味着使用外部连接,因此必须分配存储空间, 这也就是说有几个不同的编译单元应当能够引用它,所以它必须存储空间。[C++编程思想]
- const inti=10; // 这个类似宏替换,也就是说,它优化之后可能是放一个符号表里面。所有使用i的地方都用 10 代替,但是当你对 i 取址后, 没办法,编译器必须为i在常量区找个地方安身。
2013.03.11 补充:
对于本文中的内容,可以用"常量折叠"来解释,我找了些资料,大家可以看一下: