Introduction
For a long time I would have to stop and think before I put the const keyword in my code or when I read it someone else's code. A lot of programmers just don't worry about the const keyword and their justification is that they have gotten along just fine so far without it. With the aim of emphasizing improved software and code quality, this article is supposed to be a one place stop for the story of const. Beginners will hopefully find it an easy introduction to the different ways of using const and experienced programers can use the examples here as a quick reference.
Small Disclaimer/Caution
All the code presented below in the examples has been tested (if at all) very casually and so please analyse it carefully before using it. The pointer arithmetic esp. can be a cause for serious grief and care should be taken when writing production code.
const variables
When a variable is prefixed with the key word const its value cannot be changed. Any change will result in a compiler error. It makes no difference if the const keyword appears before the data type or after it.
Here is an example:
?
int
?main(
void
)

{
????
const
?
int
?i?
=
?
10
;??
//
both?variables?i?and?j?are?constants
????
int
?
const
?j?
=
?
20
;
????i?
=
?
15
;????????????
//
Error,?cannot?modify?const?object
????j?
=
?
25
;????????????
//
Error,?cannot?modify?const?object
}
const pointers
The const keyword can be used with a pointer in two ways.
const can be used to specify that the pointer is constant i.e. the memory address that the pointer points to cannot be changed. The current value at that memory address however can be changed at whim.
int
?main(
void
)?

{?
????
int
?i?
=
?
10
;
????
int
?
*
const
?j?
=
?
&
i;??
//
j?is?a?constant?pointer?to?int
????(
*
j)
++
;?????
//
The?int?value?pointed?to?by?j?can?be?modified.
????????????????
//
?i.e.?*j?=?i?=?11;
????j
++
;????????
//
ERROR:?cannot?modify?const?object?j
}
?
const can also be used with a pointer to specify that the value at the memory address cannot be changed but the pointer can be assigned to a new address.
int
?main(
void
)

{
????
int
?i?
=
?
20
;
????
const
?
int
?
*
j?
=
?
&
i;??
//
j?is?a?pointer?to?a?constant?int
????
*
j
++
;?????
//
the?pointer?value?of?j?can?be?incremented.?
??????????????
//
Note?that?the?value?at?pointer?location?j?
??????????????
//
may?be?garbage?or?cause?a?GPF?
??????????????
//
but?as?far?as?the?const?keyword?is?concerned?
??????????????
//
the?compiler?doesnt?care
????(
*
j)
++
;???
//
ERROR;?cannot?modify?const?object?*j
}
?
A good way to look at the two examples above is that in the first case j is a constant (constant pointer to int) while in the second case the *j is constant (pointer to a constant int).
The two can be combined so that neither the pointer nor its value can be changed.
?
int
?main(
void
)

{
????
int
?i?
=
?
10
;
????
const
?
int
?
*
const
?j?
=
?
&
i;?
//
j?is?a?constant?pointer?to?constant?int
????j
++
;????????????????
//
ERROR:?cannot?modify?const?object?j
????(
*
j)
++
;??????????
//
ERROR:?cannot?modify?const?object?*j????
}
const and references
A reference is merely an alias for an entity. Here are some rules:
A reference must be initialised
Once initialized, a reference cannot be made to refer to another entity.
Any change to the reference causes a change to the original entity and vice-versa.
A reference and an entity both point to the same memory location.
Here is an example to illustrate the rules above:
?
int
?main(
void
)

{?
????
int
?i?
=
?
10
;?????
//
i?and?j?are?int?variables?
????
int
?j?
=
?
20
;????
????
int
?
&
r?
=
?i;????
//
r?is?a?reference?to?i?
????
int
?
&
s;????????
//
error,?reference?must?be?initialized?
????i?
=
?
15
;?????????
//
i?and?r?are?now?both?equal?to?15.?
????i
++
;?????????
//
i?and?r?are?now?both?equal?to?16?
????r?
=
?
18
;????????
//
i?and?r?are?now?both?equal?to?18?
????r?
=
?j;????????
//
i?and?r?are?now?both?equal?to?20,?
???????????????
//
the?value?of?j.?However?r?does?not?refer?to?j.
????r
++
;????????
//
i?and?r?are?now?both?equal?to?21,?
????????????????
//
but?j?is?still?20?since?r?does?not?refer?to?j
}
Using const with a reference ensures that the reference cannot be modified. Any change however to the entity that the reference refers to will be reflected by a change in the reference. It also makes no difference whether the const keyword appears before or after the data type specifier.
Here is an example:
?
int
?main(
void
)

{
????
int
?i?
=
?
10
;
????
int
?j?
=
?
100
;
????
const
?
int
?
&
r?
=
?i;
????
int
?
const
?
&
s?
=
?j;
????r?
=
?
20
;??????????
//
error,?cannot?modify?const?object
????s?
=
?
50
;??????????
//
error,?cannot?modify?const?object
????i?
=
?
15
;??????????
//
both?i?and?r?are?now?15
????j?
=
?
25
;??????????
//
both?j?and?s?are?now?25
}
const with member functions
When the const keyword is appended to a member function declaration, then the object pointed to by the this pointer cannot be modified. This is frequently used when defining accessor methods that are only used to read the value of the object data. This is useful because developers using your code can look a the declaration and be certain that your accessor method won't modify the object in any way.
Here is an example:
?
class
?MyClass?

{?
public
:
????
int
?i;????
//
member?variable
????MyClass()

????
{
????????
//
void?constructor????
????????i?
=
?
10
;
????}
????
????
~
MyClass()

????
{
????????
//
destructor
????}
????
int
?ValueOfI()?
const
????
//
const?method?is?an?accessor?method
????
{
????????i
++
;????????
//
error,?cannot?modify?object
????????
return
?i;????
//
return?the?value?of?i
????}
}
;
Overloading with const
The const keyword can also be used with function overloading. Consider the following code:
?
class
?MyClass

{
public
:
????
int
?i;????
//
member?variable
????MyClass()

????
{
????????
//
void?constructor????
????????i?
=
?
10
;
????}
????
????
~
MyClass()

????
{
????????
//
destructor
????}
????
int
?ValueOfI()?
const
?
//
const?method?is?an?accessor?method
????
{
????????
return
?i;????????
//
return?the?value?of?i.????
????}
????
????
int
&
?ValueOfI()??????
//
method?is?used?to?set?the?
?????????????????????????
//
value?by?returning?a?reference
????
{
????????
return
?i;
????}
}
;

In the above example the two methods ValueOfI() are actually overloaded. The const keyword is actually part of the parameter list and specifies that the this pointer in the first declaration is constant while the second is not. This is a contrived example to emphasize the point that const can be used for function overloading.
In reality due to the beauty of references just the second definition of ValueOfI() is actually required. If the ValueOfI() method is used on the the right side of an = operator then it will act as an accessor, while if it is used as an l-value (left hand side value) then the returned reference will be modified and the method will be used as setter. This is frequently done when overloading operators.
?
class
?MyClass?

{?
public
:
????CPoint?MyPoint;?
????MyClass()?

????
{
????????
//
void????constructor?
????????MyPoint.x?
=
?
0
;?
????????MyPoint.y?
=
?
0
;
????}
????
~
MyClass()

????
{?
????????
//
destructor
????}
????
????MyPoint
&
?MyPointIs()????
//
method?is?used?to?set?the?value?
????????????????????????????
//
by?returning?a?reference?
????
{?
????????
return
?MyPoint;

????}
}
;

int
?main(
void
)

{
????MyClass?m;
????
int
?x1?
=
?m.MyPointIs().x;????????
//
x1?=?0
????
int
?y1?
=
?m.MyPointIs().y;????????
//
y1?=?0
????m.MyPointIs()?
=
?CPoint(
20
,
20
);???
//
m.MyPoint.x?=?20,?m.MyPoint.y?=?20
????
int
?x2?
=
?m.MyPointIs().x;????????
//
x2?=?20
????
int
?y2?
=
?m.MyPointIs().y;????????
//
y2?=?20
????CPoint?newPoint?
=
?m.MyPointIs();????
//
newPoint?=?(20,20)
}
As seen in the main function above the same MyPointIs() method is being used as an accessor where it is used to set the values of x1 & y1 and also as a setter when it is used as an l-value. This works because the myPointIs() method returns a reference that gets modified by the value on the right hand side (CPoint(20,20)).
Why should I worry about this const gubbins?
Data is passed into functions by value by default in C/C++. This means that when an argument is passed to a function a copy is made. Thus any change to the parameter within the function will not affect the parameter outside the function. The downside is that everytime the function is called a copy must be made and this can be inefficient. This is especially true if the function is called within a loop that executes (say) a 1000 times.
Here is an example:
?
class
?MyClass

{
public
:
????CPoint?MyPoint;
????MyClass()

????
{
????????
//
void?constructor
????????MyPoint.x?
=
?
0
;
????????MyPoint.y?
=
?
0
;
????}
????
~
MyClass()

????
{
????????
//
destructor
????}
????SetPoint(CPoint?point)????
//
passing?by?value?
????
{
????????MyPoint.x?
=
?point.x;
????????MyPoint.y?
=
?point.y;

????????point.x?
=
?
100
;????????
//
Modifying?the?parameter?has?
??????????????????????????????
//
no?effect?outside?the?method
????????point.y?
=
?
101
;????
????}
}
;

int
?main(
void
)

{
????MyClass?m;
????CPoint?newPoint(
15
,
15
);
????m.SetPoint(newPoint);???
//
m.Mypoint?is?(15,15)?and?so?is?newPoint.
}
As?seen?
in
?the?above?example?the?value?of?newPoint?remains?unchanged?because?the?SetPoint()?method?operates?on?a?copy?of?newPoint.?To?improve?efficieny?we?can?pass?parameters?by?reference?rather?than?by?value.?In?
this
?
case
?a?reference?to?the?parameter?
is
?passed?to?the?function?and?no?copy?
is
?needed?to?be?made.?However?now?the?problem?
is
?that?
if
?the?parameter?
is
?modified?
in
?the?method?
as
?above?then?the?variable?outside?the?method?will?also?change?leading?to?possible?bugs.?Prefixing?
const
?to?the?parameter?ensures?that?the?parameter?cannot?be?modified?from?within?the?method.

class
?MyClass

{
public
:
????CPoint?MyPoint;
????MyClass()

????
{
????????
//
void?constructor
????????MyPoint.x?
=
?
0
;
????????MyPoint.y?
=
?
0
;
????}
????
~
MyClass()

????
{
????????
//
destructor
????}
????SetPoint(
const
?CPoint
&
?point)???
//
passing?by?reference
????
{
????????MyPoint.x?
=
?point.x;
????????MyPoint.y?
=
?point.y;

????????point.x?
=
?
100
;????????
//
error,?cannot?modify?const?object
????????point.y?
=
?
101
;????????
//
error,?cannot?modify?const?object
????}
}
;

int
?main(
void
)

{
????MyClass?m;
????CPoint?newPoint(
15
,
15
);
????m.SetPoint(newPoint);????????
//
m.Mypoint?is?(15,15)?and?so?is?newPoint.
}
In this case const is used a safety mechanism that prohibits you from writing code that could come back and bite you. You should try to use const references far as possible. By declaring you method arguments as const (where appropriate), or declare const methods you are ineffect making a contract that the method will never change the value of an argument or never modify the object data. Thus other programmers can be sure that the method you provide won't clobber the data that they pass to it.
?
const傳奇
原作:Rahul Singh 翻譯:zhigang
[譯者注]有些地方按原文解釋不通,譯者根據(jù)自己的理解作了適當(dāng)修改。如有不妥之處,請告知coolgrass@sina.com或參考原文。
原文來自www.codeproject.com
簡介
當(dāng)我自己寫程序需要用到const的時候,或者是讀別人的代碼碰到const的時候,我常常會停下來想一會兒。許多程序員從來不用const,理由是即使沒用const他們也這么過來了。本文僅對const的用法稍作探討,希望能夠?qū)μ岣哕浖脑创a質(zhì)量有所幫助。
常變量
變量用const修飾,其值不得被改變。任何改變此變量的代碼都會產(chǎn)生編譯錯誤。Const加在數(shù)據(jù)類型前后均可。
例如
?
void
?main(
void
)

{
????
const
?
int
?i?
=
?
10
;????
//
i,j都用作常變量
????
int
?
const
?j?
=
?
20
;
????i?
=
?
15
;????????????
//
錯誤,常變量不能改變
????j?
=
?
25
;????????????
//
錯誤,常變量不能改變
}
常指針
Const跟指針一起使用的時候有兩種方法。
const可用來限制指針不可變。也就是說指針指向的內(nèi)存地址不可變,但可以隨意改變該地址指向的內(nèi)存的內(nèi)容。
?
void
?main(
void
)

{
????
char
*
?
const
?str?
=
?
"
Hello,?World
"
;????
//
常指針,指向字符串
????
*
str?
=
?
''
M
''
;????????????
//
可以改變字符串內(nèi)容
????str?
=
?
"
Bye,?World
"
;????????
//
錯誤,如能改變常指針指向的內(nèi)存地址
}
const也可用來限制指針指向的內(nèi)存不可變,但指針指向的內(nèi)存地址可變。
void
?main(
void
)

{
????
const
?
char
*
?str?
=
?
"
Hello,?World
"
;????
//
指針,指向字符串常量
????
*
str?
=
?
''
M
''
;????????
//
錯誤,不能改變字符串內(nèi)容
????str?
=
?
"
Bye,?World
"
;????
//
修改指針使其指向另一個字符串
????
*
str?
=
?
''
M
''
;????????
//
錯誤,仍不能改變字符串內(nèi)容
}
看完上面的兩個例子,是不是糊涂了?告訴你一個訣竅,在第一個例子中,const用來修飾指針str,str不可變(也就是指向字符的常指針);第二個例子中,const用來修飾char*,字符串char*不可變(也就是指向字符串常量的指針)。
這兩種方式可以組合起來使用,使指針和內(nèi)存內(nèi)容都不可變。
?
void
?main(
void
)

{
????
const
?
char
*
?
const
?str?
=
?
"
Hello,?World
"
;????????
//
指向字符串常量的常指針
????
*
str?
=
?
''
M
''
;????????????
//
錯誤,不能改變字符串內(nèi)容
????str?
=
?
"
Bye,?World
"
;????????
//
錯誤,不能改變指針指向的地址
}
Const和引用
引用實際上就是變量的別名,這里有幾條規(guī)則:
聲明變量時必須初始化
一經(jīng)初始化,引用不能在指向其它變量。
任何對引用的改變都將改變原變量。
引用和變量本身指向同一內(nèi)存地址。
下面的例子演示了以上的規(guī)則:
?
void
?main(
void
)

{
????
int
?i?
=
?
10
;????????????????????
//
i和j是int型變量
????
int
?j?
=
?
20
;????????
????
int
?
&
r?
=
?i;????????????????????
//
r?是變量i的引用
????
int
?
&
s;????????????????????????
//
錯誤,聲明引用時必須初始化
????i?
=
?
15
;????????????????????????
//
i?和?r?都等于15
????i
++
;????????????????????????
//
i?和?r都等于16
????r?
=
?
18
;????????????????????????
//
i?和r?都等于18
????printf(
"
Address?of?i=%u,?Address?of?r=%u
"
,
&
i,
&
r);????
//
內(nèi)存地址相同
????r?
=
?j;????????????????????????
//
i?和?r都等于20,但r不是j的引用
????r
++
;????????????????????????
//
i?和?r?都等于21,?j?仍等于20
}
用const修飾引用,使應(yīng)用不可修改,但這并不耽誤引用反映任何對變量的修改。Const加在數(shù)據(jù)類型前后均可。
例如:
void
?main(
void
)

{
????
int
?i?
=
?
10
;
????
int
?j?
=
?
100
;
????
const
?
int
?
&
r?
=
?i;
????
int
?
const
?
&
s?
=
?j;
????r?
=
?
20
;??????????
//
錯,不能改變內(nèi)容
????s?
=
?
50
;??????????
//
錯,不能改變內(nèi)容
????i?
=
?
15
;??????????
//
?i和r?都等于15
????j?
=
?
25
;??????????
//
?j和s?都等于25
}
Const和成員函數(shù)
聲明成員函數(shù)時,末尾加const修飾,表示在成員函數(shù)內(nèi)不得改變該對象的任何數(shù)據(jù)。這種模式常被用來表示對象數(shù)據(jù)只讀的訪問模式。例如:
class
?MyClass

{
????
char
?
*
str?
=
"
Hello,?World
"
;
????MyClass()

????
{
????????
//
void?constructor
????}
????
????
~
MyClass()

????
{
????????
//
destructor
????}
????
char
?ValueAt(
int
?pos)?
const
????
//
const?method?is?an?accessor?method
????
{
????????
if
(pos?
>=
?
12
)
???????????????
return
?
0
;
??
*
str?
=
?
''
M
''
;???????
//
錯誤,不得修改該對象
????????
return
?str[pos];?????
//
return?the?value?at?position?pos
????}
}
Const和重載
重載函數(shù)的時候也可以使用const,考慮下面的代碼:
class
?MyClass

{
????
char
?
*
str?
=
"
Hello,?World
"
;
????MyClass()

????
{
????????
//
void?constructor
????}
????
????
~
MyClass()

????
{
????????
//
destructor
????}
????
char
?ValueAt(
int
?pos)?
const
????
//
const?method?is?an?accessor?method
????
{
????????
if
(pos?
>=
?
12
)
???????????????
return
?
0
;
????????
return
?str[pos];????
//
return?the?value?at?position?pos
????}
????
????
char
&
?ValueAt(
int
?pos)????????
//
通過返回引用設(shè)置內(nèi)存內(nèi)容
????
{
????????
if
(pos?
>=
?
12
)
???????????????
return
?NULL;
????????
return
?str[pos];
????}
}
在上面的例子中,ValueAt是被重載的。Const實際上是函數(shù)參數(shù)的一部分,在第一個成員函數(shù)中它限制這個函數(shù)不能改變對象的數(shù)據(jù),而第二個則沒有。這個例子只是用來說明const可以用來重載函數(shù),沒有什么實用意義。
實際上我們需要一個新版本的GetValue。如果GetValue被用在operator=的右邊,它就會充當(dāng)一個變量;如果GetValue被用作一元操作符,那么返回的引用可以被修改。這種用法常用來重載操作符。String類的operator[]是個很好的例子。(這一段譯得很爛,原文如下:In reality due to the beauty of references just the second definition of GetValue is actually required. If the GetValue method is used on the the right side of an = operator then it will act as an accessor, while if it is used as an l-value (left hand side value) then the returned reference will be modified and the method will be used as setter. This is frequently done when overloading operators. The [] operator in String classes is a good example.)
?
class
?MyClass

{
????
char
?
*
str?
=
"
Hello,?World
"
;
????MyClass()

????
{
????????
//
void?constructor
????}
????
????
~
MyClass()

????
{
????????
//
destructor
????}
????
char
&
?
operator
[](
int
?pos)????????
//
通過返回引用可用來更改內(nèi)存內(nèi)容
????
{
????????
if
(pos?
>=
?
12
)
???????????????
return
?NULL;
????????
return
?str[pos];
????}
}
void
?main(
void
)

{
????MyClass?m;
????
char
?ch?
=
?m[
0
];????????
//
ch?等于?''H''
????m[
0
]?
=
?
''
M
''
;????????
//
m的成員str變成:Mello,?World
}
Const的擔(dān)心
C
/
C
++
中,數(shù)據(jù)傳遞給函數(shù)的方式默認(rèn)的是值傳遞,也就是說當(dāng)參數(shù)傳遞給函數(shù)時會產(chǎn)生一個該參數(shù)的拷貝,這樣該函數(shù)內(nèi)任何對該參數(shù)的改變都不會擴展到此函數(shù)以外。每次調(diào)用該函數(shù)都會產(chǎn)生一個拷貝,效率不高,尤其是函數(shù)調(diào)用的次數(shù)很高的時候。
例如:
class
?MyClass

{
public
:
????
int
?x;????
????
char
?ValueAt(
int
?pos)?
const
????
//
const?method?is?an?accessor?method
????
{
????????
if
(pos?
>=
?
12
)
???????????????
return
?
0
;
????????
return
?str[pos];????
//
return?the?value?at?position?pos
????}
????MyClass()

????
{
????????
//
void?constructor
????}
????
~
MyClass()

????
{
????????
//
destructor
????}
????MyFunc(
int
?y)????
//
值傳遞
????
{
????????y?
=
?
20
;
????????x?
=
?y;????
//
x?和?y?都等于?20.
????}
}
void
?main(
void
)

{
????MyClass?m;
????
int
?z?
=
?
10
;
????m.MyFunc(z);
????printf(
"
z=%d,?MyClass.x=%d
"
,z,m.x);????
//
z?不變,?x?等于20.
}
通過上面的例子可以看出,z沒有發(fā)生變化,因為MyFunc()操作的是z的拷貝。為了提高效率,我們可以在傳遞參數(shù)的時候,不采用值傳遞的方式,而采用引用傳遞。這樣傳遞給函數(shù)的是該參數(shù)的引用,而不再是該參數(shù)的拷貝。然而問題是如果在函數(shù)內(nèi)部改變了參數(shù),這種改變會擴展到函數(shù)的外部,有可能會導(dǎo)致錯誤。在參數(shù)前加const修飾保證該參數(shù)在函數(shù)內(nèi)部不會被改變。
class
?MyClass

{
public
:
????
int
?x;
????MyClass()

????
{
????????
//
void?constructor
????}
????
~
MyClass()

????
{
????????
//
destructor
????}
????
int
?MyFunc(
const
?
int
&
?y)????
//
引用傳遞,?沒有任何拷貝
????
{
????????y?
=
20
;????
//
錯誤,不能修改常變量
????????x?
=
?y????
????}
}
void
?main(
void
)

{
????MyClass?m;
????
int
?z?
=
?
10
;
????m.MyFunc(z);
????printf(
"
z=%d,?MyClass.x=%d
"
,z,m.x);????
//
z不變,?x等于10.
}
如此,const通過這種簡單安全機制使你寫不出那種說不定是什么時候就會掉過頭來咬你一口的代碼。你應(yīng)該盡可能的使用const引用,通過聲明你的函數(shù)參數(shù)為常變量(任何可能的地方)或者定義那種const method,你就可以非常有效確立這樣一種概念:本成員函數(shù)不會改變?nèi)魏魏瘮?shù)參數(shù),或者不會改變?nèi)魏卧搶ο蟮臄?shù)據(jù)。別的程序員在使用你提供的成員函數(shù)的時候,不會擔(dān)心他們的數(shù)據(jù)被改得一塌糊涂。