C++ 各种全局常量的声明方式的优缺点?
我说几点不同意见:
(1) 一般用途的const变量,绝对不应该声明在头文件,定义在cpp里,尤其是基础类型(int之类)这个和inline函数是类似的,编译器能自动处理不同cpp中有同样const的问题;如果不使用const变量的地址,这个const变量最终会被完全优化掉,不占静态存储空间。而如果定义成extern,会导致编译器不得不为这个变量分配一个地址,并且在使用的时候强制地去从地址取得值,这会导致极大的性能下降。比如:
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> a </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">16</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">inline</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> sqr</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="pln" style="color: rgb(0, 0, 0);">x</span><span class="pun" style="color: rgb(0, 0, 0);">)</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">{</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">return</span><span class="pln" style="color: rgb(0, 0, 0);"> a </span><span class="pun" style="color: rgb(0, 0, 0);">*</span><span class="pln" style="color: rgb(0, 0, 0);"> a</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L5" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">}</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L6" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L7" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);">myFunc</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="pln" style="color: rgb(0, 0, 0);">sqr</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="pln" style="color: rgb(0, 0, 0);">a</span><span class="pun" style="color: rgb(0, 0, 0);">));</span></li></ol>
在这里,编译器可以把整个sqr(a)都优化掉,替换成一个立即数256。而如果a定义成了extern,这里就必须是读数值、乘法、传参数的过程了。
C++中编译器对于const变量做了充分的优化,它完全可以取代#define常量的一切功能。
比如:
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> MAX_N </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">100</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> myArray</span><span class="pun" style="color: rgb(0, 0, 0);">[</span><span class="pln" style="color: rgb(0, 0, 0);">MAX_N</span><span class="pun" style="color: rgb(0, 0, 0);">];</span></li></ol>
在遇到使用const变量的时候,编译器如果已知const变量的值,就会直接把值代入进行优化,和#define是完全一样的。如果最后const变量没有取地址之类的操作,编译器就会认为这个变量没有被实际引用,于是在生成代码的最后把整个const变量去掉。
const变量如果不另加定义,默认就是static的。特意去定义成extern只会破坏编译器效率。这个和C是完全不同的,所以不要把C的法则想当然地用进来。
不过对于const变量类型是复杂类型(类)的时候,static的类型可能会导致每个cpp里面都生成一个对象,这时候应该考虑extern。
(2) const变量和#define宏相比最大的好处其实不在于类型检查,而在于可以有命名空间
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">namespace</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="typ" style="color: rgb(43, 145, 175); font-weight: bold;">MyNameSpace</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">{</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="com" style="color: gray;">#define</span><span class="pln" style="color: rgb(0, 0, 0);"> myMacro </span><span class="lit" style="color: maroon;">15</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">class</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="typ" style="color: rgb(43, 145, 175); font-weight: bold;">MyClass</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="pun" style="color: rgb(0, 0, 0);">{</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L5" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">public</span><span class="pun" style="color: rgb(0, 0, 0);">:</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L6" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">static</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> myConst </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">15</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L7" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="com" style="color: gray;">#define</span><span class="pln" style="color: rgb(0, 0, 0);"> myMacro2 </span><span class="lit" style="color: maroon;">16</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L8" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="pun" style="color: rgb(0, 0, 0);">}</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L9" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> myConst2 </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">16</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">}</span></li></ol>
myConst必须用MyNameSpace::MyClass::myConst来引用,myConst2对应MyNameSpace::myConst2,而#define的两个常量永远都是myMacro和myMacro2,这意味着#define的常量重名的可能性要大得多,而且很容易破坏OOP结构。
(3) 特殊情况下#define和const比可能会引起问题。比如函数重载:
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">void</span><span class="pln" style="color: rgb(0, 0, 0);"> myFunc</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> x</span><span class="pun" style="color: rgb(0, 0, 0);">)</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">{</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">...</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">}</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L5" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">void</span><span class="pln" style="color: rgb(0, 0, 0);"> myFunc</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">short</span><span class="pln" style="color: rgb(0, 0, 0);"> x</span><span class="pun" style="color: rgb(0, 0, 0);">)</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L6" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">{</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L7" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">...</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L8" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="pun" style="color: rgb(0, 0, 0);">}</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L9" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">short</span><span class="pln" style="color: rgb(0, 0, 0);"> myConst </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">128</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="com" style="color: gray;">#define</span><span class="pln" style="color: rgb(0, 0, 0);"> myMacro </span><span class="lit" style="color: maroon;">128</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);">myFunc</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="pln" style="color: rgb(0, 0, 0);">myConst</span><span class="pun" style="color: rgb(0, 0, 0);">);</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);">myFunc</span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="pln" style="color: rgb(0, 0, 0);">myMacro</span><span class="pun" style="color: rgb(0, 0, 0);">);</span></li></ol>
#define由于没有类型,会去调用int版本的函数。这是const强制类型检查的好处
(4) const类型的编译效率比较好
因为编译器在编译的时候已经知道这个符号对应的是某类型的数了,编译会快一些。对运行影响不大。还有:
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="com" style="color: gray;">#define</span><span class="pln" style="color: rgb(0, 0, 0);"> MAXN </span><span class="pun" style="color: rgb(0, 0, 0);">(</span><span class="lit" style="color: maroon;">12</span><span class="pun" style="color: rgb(0, 0, 0);">*</span><span class="lit" style="color: maroon;">14</span><span class="pun" style="color: rgb(0, 0, 0);">)</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> MAX_N </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">12</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="pun" style="color: rgb(0, 0, 0);">*</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="lit" style="color: maroon;">14</span><span class="pun" style="color: rgb(0, 0, 0);">;</span></li></ol>
使用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
这些功能目前还没有其他语法可以替代。
宏定义可以叫做全局常量吗?这我倒不清楚了。它就是一个常量而已,而且理论上是没什么不好的,宏定义只是文本复制。
如果你说的是希望整个工程项目都能用到的常量,的确大部分都会用宏定义的方式定义出来。
而枚举常量在Linux的内核代码中也很常见,不过更多是出现于定义一个错误代码,即希望1是代码错误a,2是代表错误b,但是很少作为全局的常量来声明。
我看来实际上代码中会出现全局的常量的情况很少,(我指的是占内存空间的,不是说像宏定义那样的。)因为非得要定义一个全局的常量的话,将它用宏定义声明会好很多。印象中只见过没办法只能定义全局变量的,可能我读的代码量不多。
参考后面几位的答案,再确认了一番。
在C++里头确实不提倡用#define,然后对Effective C++说的,也小怀疑了一下,我觉得是这样的,对于一个可以成为立即数的常量来说(即大小小于寄存器可以存的数的大小),define还是有优势的,翻译成汇编就知道了,立即数不用另外开辟空间来存储,我觉得编译器会做好这个优化(Effective C++说的也不完全对啊!)。但是对于一个不能作为立即数的常量来说,编译器编译代码的时候,一定会在data段再开辟一段空间专门放这个常量,然后用到的时候就会寻址找到这个常量来使用,用define的时候,用多少次,data段内就会出现多少次,而用const来定义的时候,无论用多少次,data段内也只会有一次,所以节省了代码空间。
-
@SunnyLee 我后面答案里面大致写了。总的来说就是const的值会自动代入到使用的位置成为立即数,和inline函数有点像。而define在多大程度上避免重复计算,是和编译器有关的。 – 灵剑2012 2012-09-16
-
@SunnyLee 再就是复杂类型上的问题了,比如#define pairData (pair<int,int>(12,15)) 和 const pair<int,int> pairData = pair<int,int>(12,15); 如果定义成宏,每次使用的地方都要调一次构造函数;而对const来说,只要调用一次就够了。 – 灵剑2012 2012-09-16
1)宏不是全局常量,只是预编译,编译器在编译前做字符串替换。
2)C++中用const声明常量 const type name = value; 这个比宏好的是:其有类型检查。
3)enum 不是定义常量语义,是一种定义只能有某种取值类型语义。
4)全局变/常量千万不要定义在头文件中,而是declare在h文件,define在c/cpp文件中,如:
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="com" style="color: gray;">//t.c</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> g_int </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="lit" style="color: maroon;">10</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="com" style="color: gray;">//t.h</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">extern</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">const</span><span class="pln" style="color: rgb(0, 0, 0);"> </span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> g_int</span><span class="pun" style="color: rgb(0, 0, 0);">;</span></li></ol>
5)全局变量/常量应该全部定义在一个文件中,如果定义在不同的文件中,那么编译器将无法保证初始化顺序。
下面的这种情况可能会让a初始异常。
<ol class="linenums" style="list-style-position: outside; margin: 0px; padding: 0px 0px 0px 10px; border: none;"><li class="L0" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="com" style="color: gray;">//a.c</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L1" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> a </span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="pln" style="color: rgb(0, 0, 0);"> b</span><span class="pun" style="color: rgb(0, 0, 0);">*</span><span class="lit" style="color: maroon;">2</span><span class="pun" style="color: rgb(0, 0, 0);">;</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L2" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L3" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="com" style="color: gray;">//b.c</span><span class="pln" style="color: rgb(0, 0, 0);"></span>
</li><li class="L4" style="list-style: decimal; margin: 0px; padding: 0px 0px 0px 10px; border: none; line-height: 20px; color: rgb(153, 153, 153); background-color: rgb(250, 250, 250);"><span class="pln" style="color: rgb(0, 0, 0);"></span><span class="kwd" style="color: rgb(0, 0, 139); font-weight: bold;">int</span><span class="pln" style="color: rgb(0, 0, 0);"> b</span><span class="pun" style="color: rgb(0, 0, 0);">=</span><span class="lit" style="color: maroon;">10</span><span class="pun" style="color: rgb(0, 0, 0);">;</span></li></ol>
6)全局的变量/常量最好放在namespace中,以防避免变量冲突。尤其是和local variable的冲突,编译器和运行都是不会出错的。
宏常量只是编译前的文本替换而已,没有类型检查,宏常量也不能调试。所以宏很容易引入错误。
#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}。枚举类型定义常量可以提高代码的可读性。