个人账号如何注册网站,广州专业网站改版设计公司,西安seo服务外包,做网站网站危险吗const和#define
这个条款或许改为“宁可以编译器替换预处理器”比较好#xff0c;因为或许#define不被视为语言的一部分。那正是它的问题所在。当你做出这样的事情#xff1a;
#define ASPECT RATIO 1.653
记号名称ASPECT_RATIO也许从未被编译器看见#xff1b;也许在编译…const和#define
这个条款或许改为“宁可以编译器替换预处理器”比较好因为或许#define不被视为语言的一部分。那正是它的问题所在。当你做出这样的事情
#define ASPECT RATIO 1.653
记号名称ASPECT_RATIO也许从未被编译器看见也许在编译器开始处理源码之前它就被预处理器移走了。于是记号名称ASPECT RATIO有可能没进入记号表(symbol table)内。于是当你运用此常量但获得一个编译错误信息时可能会带来困惑因为这个错误信息也许会提到1.653而不是 ASPECT RATIO。如果ASPECT RATIO被定义在一个非你所写的头文件内你肯定对1.653以及它来自何处毫无概念于是你将因为追踪它而浪费时间。这个问题也可能出现在记号式调试器(symbolic debugger)中原因相同你所使用的名称可能并未进入记号表(symboltable)。解决之道是以一个常量替换上述的宏(#define)
const double AspectRatio 1.653; //大写名称通常用于宏
//因此这里改变名称写法。 作为一个语言常量AspectRatio肯定会被编译器看到当然就会进入记号表内。此外对浮点常量(floating point constant就像本例)而言使用常量可能比使用#define导致较小量的码因为预处理器“盲目地将宏名称ASPECT_RATIO替换为1.653”可能导致目标码(object code)出现多份1.653若改用常量AspectRatio绝不会出现相同情况。
以常量替换#define的两种特殊情况
第一是定义常量指针
由于常量定义式通常被放在头文件内以便被不同的源码含入因此有必要将指针而不只是指针所指之物)声明为const。
例如着要在文件内定义一个常量的(不变的)char*-based字符串你必须写 const两次
const char* const authorName-Scott Meyers; 关于const的意义和使用(特别是当它与指针结合时)条款3有完整的讨论。这里值得先提醒你的是string对象通部比其前辈chart-based合宜所以上选的authorName 往往定义成这样更好些
const std::string authorName(Scott Meyers); 第二个是class专属常量
为了将常量的作用域(scope)限制于class内你必须让它成为class的一个成员而为确保此常量至多只有一份实体你必须让它成为一个static成员
class GamePlayer {
private:
static const int NumTuns 5; //常量声明式
int scores [NumTurns]; //使用该常量
}
然而你所看到的是NumTurns的声明式而非定义式。通常C要求你对你所使用的任何东西提供一个定义式但如果它是个class专属常量又是static且为整数类型(integral type例如ints,charsbools)则需特殊处理。只要不取它们的地址你可以声明并使用它们而无须提供定义式。但如果你取某个class专属常量的地址或纵使你不取其地址而你的编译器却(不正确地)坚持要看到一个定义式你就必须另外提供定义式如下
const int GamePlayer::NumTurns; //NumTurns的定义
//下面告诉你为什么没有给予数值
请把这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值(例如先前声明Numturns时为它设初值5)因此定义时不可以再设初值。顺带一提请注意我们无法利用#define 创建一个class 专属常量因为#defines并不重视作用域(scope)。一旦宏被定义它就在其后的编译过程中有效(除非在某处被#undef)。这意味#defines不仅不能够用来定义class专属常量也不能够提供任何封装性也就是说没有所谓private #define这样的东西。而当然const成员变量是可以被封装的NumTurns就是。旧式编译器也许不支持上述语法它们不允许static成员在其声明式上获得初值。此外所谓的“in-class 初值设定”也只允许对整数常量进行。如果你的编译器不支持上述语法你可以将初值放在定义式
class CostEstimate {
private:
static const double FudgeFactor; //static class 常量声明
//位于头文件内
}
const double CostEstimate::FudgeFactor 1.35static class常量定义位于实现文件内 这几乎是你在任何时候唯一需要做的事。
enum和#define
上面这例子几乎是你在任何时候唯一需要做的事。唯一例外是当你在class 编译期间需要一个class常量值例如在上述的GamePlayer:scores的数组声明式中(是的编译器坚持必须在编译期间知道数组的大小)。这时候万一你的编译器(错误地)不允许“static 整数型class 常量”完成“in class初值设定”可改用所谓的the enumhack”补偿做法。其理论基础是“一个属于枚举类型(enumerated type)的数值可权充ints被使用”于是GamePlayer可定义如下
class GamePlayer {
private:
enum ( NumTurns5 ); //the enum hack—令 NumTurns
//成为5的一个记号名称.
int scores [NumTurns]; //这就没问题了.
}; 基于数个理由 enum back值得我们认识。第一enum hack的行为某方面说比较像 #define而不像const有时候这正是你想要的。例如取一个const的地址是合法的但取一个enum的地址就不合法而取一个#define的地址通常也不合法。
如果你不想让别人获得一个pointer或referenee指向你的某个整数常量enum可以帮助你实现这个约束。此外虽然优秀的编译器不会为“整数型const对象”设定另外的存物空间(除非你创建一个pointer或reference指向该对象)不够优秀的编译器却能如此而这可能是你不想要的。enum 和define一样绝不会导致非必要的内有分配。 认识enum hack的第二个理由纯粹是为了实用主义。许多代码用了它所以看到它时你必须认识它。事实上enum hack”是template metaprogramming (模板元编程见条款48)的基础技术。 把焦点拉回预处理器。
inline和#define
另一个常见的#define误用情况是以它实现宏(macros)。宏看起来像函数但不会招致函数调用(function call)带来的额外开销。下面这个宏夹带着宏实参调用函数
//以a和b的较大值调用f
#define CALL_WITH_MAX(a,b) f((a) (b)? (a)(b)) 这般长相的宏有着太多缺点光是想到它们就让人痛苦不堪。 无论何时当你写出这种宏你必须记住为宏中的所有实参加上小括号否则某些人在表达式中调用这个宏时可能会遭遇麻烦。但纵使你为所有实参加上小括号看看下面不可思议的事情
int a5,b0;
CALL_WITH MAX(ta, b); //a 被累加二次
CALL WITH MAX(ab10); //a被累加一次 在这里调用f之前a的递增次数竟然取决于“它被拿来和谁比较” 幸运的是你不需要对这种无聊事情提供温床。你可以获得宏带来的效率以及一般函数的所有可预料行为和类型安全性(typesafety)——只要你写出template inline函数(见条款30)
templatetypename r //由于我们不知道
inline void callWithMax(const T a, const T b)//T是什么所以采用( 一 f(ab? a: b) //pass by reference-to-const.
//见条款 20.
这个template产出一整群函数每个函数都接受两个同型对象并以其中较大者调用f。这里不需要在函数本体中为参数加上括号也不需要操心参数被核算(求值)多次……等等。此外由于cal1withMax是个真正的函数它遵守作用域(scope)和访问规则。例如你绝对可以写出一个“class内的private inline函数”。一般而言宏无法完成此事。 Hlefine 有了consts、enums 和inlines我们对预处理器(特别是#define)的需求降低了但并非完全消除。#include仍然是必需品而#ifdef/#ifndef也继续扮演控制编译的重要角色。目前还不到预处理器全面引退的时候但你应该明确地给予它更长更频繁的假期。
总结
对于单纯常量最好以const对象或enums替换#defines。 对于形似函数的宏(macros)最好改用inline函数替换#defines。