C++ 各种全局常量的声明方式的优缺点?
2012-11-23 08:30
411 查看
C++ 各种全局常量的声明方式的优缺点?
蓝色梦幻5 票
1607
在一些比较大的工程中,经常会用到很多全局常量,这些常量的声明方式有很多种。
比如:宏常量 #define XXXXX = 0x00
枚举常量 enum{ YYYYY = 0, ZZZZZ = 1 };
……
除此之外还有什么常量声明方式?
这些常量声明方式有什么优缺点?
在什么情况下该用哪种常量?
评论 (1) • 分享 • 链接 • 2012-09-12
0
const 声明, 和枚举比较常用,宏定义会出现不安全的特性。当然你可以用一些熟悉函数生成常量比如是圆周率pi就可以直接用反正切的得到pi的值,而且精度很高! – chuanchuan608 2012-09-18
9个答案
票 数
灵剑2012
7 票
4539
最佳答案
我说几点不同意见:
(1) 一般用途的const变量,绝对不应该声明在头文件,定义在cpp里,尤其是基础类型(int之类)这个和inline函数是类似的,编译器能自动处理不同cpp中有同样const的问题;如果不使用const变量的地址,这个const变量最终会被完全优化掉,不占静态存储空间。而如果定义成extern,会导致编译器不得不为这个变量分配一个地址,并且在使用的时候强制地去从地址取得值,这会导致极大的性能下降。比如:
const int a = 16; inline int sqr(x) { return a * a; } myFunc(sqr(a));
在这里,编译器可以把整个sqr(a)都优化掉,替换成一个立即数256。而如果a定义成了extern,这里就必须是读数值、乘法、传参数的过程了。
C++中编译器对于const变量做了充分的优化,它完全可以取代#define常量的一切功能。
比如:
const int MAX_N = 100; int myArray[MAX_N];
在遇到使用const变量的时候,编译器如果已知const变量的值,就会直接把值代入进行优化,和#define是完全一样的。如果最后const变量没有取地址之类的操作,编译器就会认为这个变量没有被实际引用,于是在生成代码的最后把整个const变量去掉。
const变量如果不另加定义,默认就是static的。特意去定义成extern只会破坏编译器效率。这个和C是完全不同的,所以不要把C的法则想当然地用进来。
不过对于const变量类型是复杂类型(类)的时候,static的类型可能会导致每个cpp里面都生成一个对象,这时候应该考虑extern。
(2) const变量和#define宏相比最大的好处其实不在于类型检查,而在于可以有命名空间
namespace MyNameSpace { #define myMacro 15 class MyClass { public: const static int myConst = 15; #define myMacro2 16 } const int myConst2 = 16; }
myConst必须用MyNameSpace::MyClass::myConst来引用,myConst2对应MyNameSpace::myConst2,而#define的两个常量永远都是myMacro和myMacro2,这意味着#define的常量重名的可能性要大得多,而且很容易破坏OOP结构。
(3) 特殊情况下#define和const比可能会引起问题。比如函数重载:
void myFunc(int x) { ... } void myFunc(short x) { ... } const short myConst = 128; #define myMacro 128 myFunc(myConst); myFunc(myMacro);
#define由于没有类型,会去调用int版本的函数。这是const强制类型检查的好处
(4) const类型的编译效率比较好
因为编译器在编译的时候已经知道这个符号对应的是某类型的数了,编译会快一些。对运行影响不大。还有:
#define MAXN (12*14) const int MAX_N = 12 * 14;
使用MAXN的时候,编译器每次都得对表达式求值;MAX_N的话,只求了一次。
(5) const类型不容易出错
这个问题是老生常谈了,#define MACRO 12+13这种写法会出问题,必须加括号。const就没这个问题。
(6) enum
enum其实可以看做是定义了一族const变量。它们同样也有命名空间,同样也有变量类型(不过变量类型是enum XXX),额外的,enum定义的常量不会使用额外的内存空间。缺点在于:enum定义的常量,类型是enum XXX,不是int。虽然可以和int型数值无限制地转换,但毕竟不是int,使用上不是那么方便,比如不能直接做加减乘除之类。
(7) #define的优势
#define在C++中的作用一般已经不是定义常量或者宏了,如前文所说这些功能应该由const和inline函数来代替。#define在C++中可以用来定义一些快捷语法,或者是根据编译选项不同生成不同内容:
#define L(x) L##x
L("abcde") // L"abcde"
#ifdef _UNICODE
#define MyFunc MyFuncW
#else
#define MyFunc MyFuncA
#endif
这些功能目前还没有其他语法可以替代。
评论 (0) • 链接 •
2012-09-16
SunnyLee
6 票
223
宏定义可以叫做全局常量吗?这我倒不清楚了。它就是一个常量而已,而且理论上是没什么不好的,宏定义只是文本复制。
如果你说的是希望整个工程项目都能用到的常量,的确大部分都会用宏定义的方式定义出来。
而枚举常量在Linux的内核代码中也很常见,不过更多是出现于定义一个错误代码,即希望1是代码错误a,2是代表错误b,但是很少作为全局的常量来声明。
我看来实际上代码中会出现全局的常量的情况很少,(我指的是占内存空间的,不是说像宏定义那样的。)因为非得要定义一个全局的常量的话,将它用宏定义声明会好很多。印象中只见过没办法只能定义全局变量的,可能我读的代码量不多。
参考后面几位的答案,再确认了一番。
在C++里头确实不提倡用#define,然后对Effective C++说的,也小怀疑了一下,我觉得是这样的,对于一个可以成为立即数的常量来说(即大小小于寄存器可以存的数的大小),define还是有优势的,翻译成汇编就知道了,立即数不用另外开辟空间来存储,我觉得编译器会做好这个优化(Effective C++说的也不完全对啊!)。但是对于一个不能作为立即数的常量来说,编译器编译代码的时候,一定会在data段再开辟一段空间专门放这个常量,然后用到的时候就会寻址找到这个常量来使用,用define的时候,用多少次,data段内就会出现多少次,而用const来定义的时候,无论用多少次,data段内也只会有一次,所以节省了代码空间。
SunnyLee
编辑于 2012-09-12
评论 (5) • 链接 •
2012-09-12
0
这话怎么说?为什么说优化后更好呢?优化会处理什么呢?能稍微讲一下吗?谢谢! – SunnyLee2012-09-16
0
@SunnyLee 我后面答案里面大致写了。总的来说就是const的值会自动代入到使用的位置成为立即数,和inline函数有点像。而define在多大程度上避免重复计算,是和编译器有关的。 – 灵剑20122012-09-16
1
@SunnyLee 再就是复杂类型上的问题了,比如#define pairData (pair<int,int>(12,15)) 和 const pair<int,int> pairData = pair<int,int>(12,15); 如果定义成宏,每次使用的地方都要调一次构造函数;而对const来说,只要调用一次就够了。 – 灵剑2012 2012-09-16
0
你的答案说得太好了。。看来对C++还是不熟啊。。还有对编译器实际做的工作确实还是有不清楚的地方。 – SunnyLee 2012-09-16
显示更多隐藏的评论
haoel
6 票
204
1)宏不是全局常量,只是预编译,编译器在编译前做字符串替换。
2)C++中用const声明常量 const type name = value; 这个比宏好的是:其有类型检查。
3)enum 不是定义常量语义,是一种定义只能有某种取值类型语义。
4)全局变/常量千万不要定义在头文件中,而是declare在h文件,define在c/cpp文件中,如:
//t.c const int g_int =10; //t.h extern const int g_int;
5)全局变量/常量应该全部定义在一个文件中,如果定义在不同的文件中,那么编译器将无法保证初始化顺序。
下面的这种情况可能会让a初始异常。
//a.c int a = b*2; //b.c int b=10;
6)全局的变量/常量最好放在namespace中,以防避免变量冲突。尤其是和local variable的冲突,编译器和运行都是不会出错的。
评论 (1) • 链接 •
2012-09-12
1
第四点不同意,除非要用const变量的地址来做判断,否则应该定义在头文件里面。定义在头文件里面就相当于inline了,用到const的地方才可以有充分的优化。 – 灵剑2012 2012-09-16
mario
3 票
1065
宏常量只是编译前的文本替换而已,没有类型检查,宏常量也不能调试。所以宏很容易引入错误。
#define PI 3.14159
如果改为 #define PI asdf //没有类型检查的 也不会报错。
const double PI = 3.14159;
使用 double area = PIrr; //类型安全
在C++ 中常量不要使用宏去定义,如果是常量 在c++可以使用const 常量 去代替C语言中的宏。
使用枚举常量的优点是有清晰的语意,并且同个枚举类型中的各个常量必须是有关联的。例如
enum Color {RED,GREEN}; 这样你就知道这些是代表颜色的值。不要定义不论不类的枚举类型例如:enum Color {LENGTH,RED}。枚举类型定义常量可以提高代码的可读性。
评论 (0) • 链接 •
2012-09-12
江南烟雨
3 票
2304
尽量少用宏,具体可参考《Effecitve C++》条款2:
最好以const对象或enums替换#defines
评论 (0) • 链接 •
2012-09-12
Macro
3 票
31
(1)#define pi 3.14 就是在编译的时候简单的替换,我觉得叫常数。
(2)常量和变量相对。变量可变,常量不可变。
(3)全局常量一般使用就是头文件声明,c/cpp定义。
(4)枚举变量主要就是给多个相似的名字编了几个号。只能是整形。
评论 (0) • 链接 •
2012-09-13
bdmh
0 票
41
这是宏定义,不是常量,常量是const,防止被修改,宏变量方便统一,维护
评论 (0) • 链接 •
2012-09-12
owenyang
0 票
1
《Effecitve C++》里建议用const
评论 (0) • 链接 •
2012-09-16
Goss
0 票
41
我个人观点是尽量少用,全局常量太多会污染命名空间的。
评论 (0) • 链接 •
2012-09-23
相关文章推荐
- C++ 各种全局常量的声明方式的优缺点?
- C++ 各种全局常量的声明方式的优缺点?
- C++ 各种全局常量的声明方式的优缺点
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- 【转】C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区,常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- (转)C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
- C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储
- Linux_C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区