0常量Word格式.docx
《0常量Word格式.docx》由会员分享,可在线阅读,更多相关《0常量Word格式.docx(11页珍藏版)》请在冰点文库上搜索。
{\
}
{
INTI_RECT_VALUE(rect[index].a,rect[index].b);
0-3:
使用宏时,不允许参数发生变化。
如下用法可能导致错误。
#defineSQUARE(a)((a)*(a))
inta=5;
intb;
b=SQUARE(a++);
//结果:
a=7,即执行了两次增1。
正确的用法是:
b=SQUARE(a);
a++;
a=6,即只执行了一次增1。
0-4:
在C++程序中尽量以const和inline取代#define。
换言之,尽量以编译器(compiler)取代预编译器(preprocessor)。
C++语言可以用const来定义常量,也可以用#define来定义常量。
但是前者比后者有更多的优点:
(1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会
产生意料不到的错误(边际效应)。
#defineDEFAULT_WIDTH320;
//DEFAULT_WIDTH没有进入符号表
constLONGDEFAULT_WIDTH=320;
//DEFAULT_WIDTH进入符号表
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
inline函数可以获得宏带来的高效率以及函数带来的可预期行为和参数类型检验。
可以通过下面的例子来说明。
下面是这个程序的输出,它完全不是我们想从真正的函数期望得到的结果:
但是如果将上面那条宏该为
inlineintband(intx){return((x)>
5)&
&
(x)<
10}?
(x):
0;
就可以得到预期的结果。
如果不需要参数检查,可以使用template修正这一问题,做到尽善尽美:
template<
classT>
inlineconstT&
band(constT&
x)
{
return((x)>
上面写法使用了引用传递(返回值与参数),提高了函数的效率。
0-5:
const更大的魅力是它可以修饰指针、数组、变量、函数的参数、函数返回值,甚至函数的定义体,推荐尽量使用const。
(1)可以使指针成为const指针,如果程序员以后想在程序代码中改变这种指针的使用,编译器将给出通知,这大大增加了安全性。
当使用带有指针的const时,有两种选择:
或者const修饰指针正指向对象,或者const修饰存储在指针本身的地址里。
两者区分的原则是“最靠近”原则。
char*p=“Hello”;
//non-constpointer,non-constdata
constchar*p=“Hello”;
//non-constpointer,constdata,指向const的指针,这里可以不需要初始化,因为说p可以指向任何东西(那是说,它不是一个const),但它所指的东西是不能被改变的。
charconst*p=“Hello”;
//non-constpointer,constdata,不推荐这种写法。
char*constp=“Hello”;
//constpointer,non-constdata,p是一个指针,这个指针是指向char的const指针。
constchar*constp=“Hello”;
//constpointer,constdata。
(1-1)constchar*p=“Hello”;
constchar*q=“Hello”;
*p=*q;
//不合法
(1-2)char*constp=“Hello”;
p=q;
(2)用const修饰函数的参数。
如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用。
virtualHRESULTExecuteAddXpdr(/*[in]*/constXPDRTERSTRUCT*constpXpdrParams);
ST_ErrorCode_tSTBLIT_Init(constST_DeviceName_tDeviceName,constSTBLIT_InitParams_t*constInitParams_p);
如果函数体内的语句试图改动pXpdrParams的内容或者pXpdrParams本身地址,编译器将指出错误。
下面是函数重载的写法,对XpdrNoInRegion采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const修饰,下面有些const修辞是多余的。
virtualHRESULTExecuteModifyXpdr(/*[in]*/constINTXpdrNoInRegion,
/*[in]*/constXPDRTERSTRUCT*constpXpdrParams);
/*[in]*/constINTXpdrFreq,
/*[in]*/constBYTEBandMode);
/*[in]*/constINTFreqOffset,
/*[in]*/constBYTEBandMode);
virtualHRESULTExecuteDeleteXpdr(/*[in]*/constINTXpdrNoInRegion);
(3)对于非内部数据类型的参数而言,象voidFunc(Aa)这样声明的函数注定效率比较底。
因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。
为了提高效率,可以将函数声明改为voidFunc(A&
a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。
但是函数voidFunc(A&
a)存在一个缺点:
“引用传递”有可能改变参数a,这是我们不期望的。
解决这个问题很容易,加const修饰即可,因此函数最终成为voidFunc(constA&
a)。
(4)用const修饰函数的返回值。
(4-A)如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。
例如函数
constchar*GetString(void);
如下语句将出现编译错误:
char*str=GetString();
正确的用法是
constchar*str=GetString();
(4-B)如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
例如不要把函数intGetInt(void)写成constintGetInt(void)。
同理不要把函数AGetA(void)写成constAGetA(void),其中A为用户自定义的数据类型。
如果返回值不是内部数据类型,将函数AGetA(void)改写为constA&
GetA(void)的确能提高效率。
但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
(4-C)函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
例如:
classA
...
A&
operate=(constA&
other);
//赋值函数
};
Aa,b,c;
//a,b,c为A的对象
…
a=b=c;
//正常的链式赋值
(a=b)=c;
//不正常的链式赋值,但合法
如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。
上例中,语句a=b=c仍然正确,但是语句(a=b)=c则是非法的。
(4-D)任何不会修改数据成员的函数都应该声明为const类型。
如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数。
编译器将指出GetCount函数中的错误。
classStack
public:
voidPush(intelem);
intPop(void);
intGetCount(void)const;
//const成员函数
private:
intm_num;
intm_data[100];
intStack:
:
GetCount(void)const
++m_num;
//编译错误,企图修改数据成员m_num
Pop();
//编译错误,企图调用非const函数
returnm_num;
const成员函数的声明看起来怪怪的:
const关键字只能放在函数声明的尾部,大概
是因为其它地方都已经被占用了。
(5)const还可以用来修辞一维或多维常量数组。
下面HfilterBuffer和gridheader数组的数据就不能在更改了。
constU8HFilterBuffer[STBLIT_HFILTER_COEFFICIENTS_SIZE*STBLIT_DEFAULT_NUMBER_FILTERS]=
/*Filter921*/
0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x00,0xff,0x07,0x3d,0xfc,0x01,0x00,
/*Filter1024*/
0xff,0x03,0xfd,0x08,0x3e,0xf9,0x04,0xfe,
externconstU8HFilterBuffer[STBLIT_HFILTER_COEFFICIENTS_SIZE*STBLIT_DEFAULT_NUMBER_FILTERS];
//fillgridheaderinformation
staticconstUINTgridheader[]=
IDD_STR_ALLSEL,
ID_INDICATOR_CARD_CARDID,
ID_INDICATOR_CARD_CARDLEVEL,
ID_INDICATOR_CARD_ISSUSPENDED,
ID_INDICATOR_CARD_ISSALE,
ID_INDICATOR_USER_USRNAME,
ID_INDICATOR_USER_USRID,
ID_INDICATOR_CARD_SALEDATETIME,
ID_INDICATOR_CARD_STOREDATETIME,
ID_INDICATOR_CARD_STOPDATETIME,
ID_INDICATOR_CARD_USRMSG,
(6)通过上面的分析,就不难理解下面几个例子了。
staticconstST_Revision_tRevision399="
STV0399-LLA_REL_2.1.0"
;
staticvoidCopy1DNoOverlapMPEG1DBlockMove(void*constSrcAddr_p,
void*constDestAddr_p,
constU32Size);
error=STTUNER_Open(TunerDeviceName[Params_p->
Tuner],
(constSTTUNER_OpenParams_t*)&
OpenParams,
&
TUNERHandle);
0-6:
明智地运用inline。
inline函数,看起来像函数,动作起来像函数,比宏好得多,而且可以调用它们,不需要蒙受函数调用所带来的额外负担。
由于每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现多个循环,那么执行函数体内代码的时间要比函数调用的开销大。
下面函数init_programs和find_program函数比较简单,而且没有多个循环,推荐用内联函数的写法。
inline是一种“用于实现的关键字”,必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。
定义在类声明之中的成员函数将自动地成为内联函数,因此这种情况可以省略书写inline。
structCPrograms
PROGRAM_INFO*m_programs[MAX_PROGRAM_NUMBER];
intm_ProgramCount;
_inlinevoidinit_programs(void)
{
m_ProgramCount=0;
}
_inlineBOOLfind_program(constDWORDdwProgramNumber,
int*pIndex)
for(inti=0;
i<
m_ProgramCount;
i++)
{
if(m_programs[i]->
program_number==dwProgramNumber)
{
*pIndex=i;
returnTRUE;
returnFALSE;
0-7:
如何使得某些常量只在类中有效。
由于#define定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。
但是应该注意const数据成员的初始化只能在类构造函数的初始化表中进行。
如下例子不符合规范。
因为类的对象未被创建时,编译器不知道SIZE的值是什么。
constintSIZE=100;
//错误,企图在类声明中初始化const数据成员
intarray[SIZE];
//错误,未知的SIZE
应如下书写:
{...
A(intsize);
//构造函数
constintSIZE;
A:
A(intsize):
SIZE(size)//构造函数的初始化表
Aa(100);
Ab(200);