簡介
當我自己寫程序需要用到const的時候,或者是讀別人的代碼碰到const的時 候,我常常會停下來想一會兒。許多程序員從來不用const,理由是即使沒用const他們也這麼 過來了。本文僅對const的用法稍作探討,希望能夠對提高軟件的源代碼質量有所幫助。
常變量
變量用const修飾,其值不得被改變。任何改變此變量的代碼都會產生編譯錯 誤。Const加在數據類型前後均可。
例如
void main(void)
常指針
{
const int i = 10; //i,j都用作常變量
int const j = 20;
i = 15; //錯誤,常變量不能改變
j = 25; //錯誤,常變 量不能改變
}
Const跟指針一起使用的時候有兩種方法。
const可用來限制指針不可變。也就是說指針指向的內存地址不可變,但可以隨意改變該 地址指向的內存的內容。
void main(void)
const也可用來限制指針指向的內存不可變,但指針指向的內存地址可變。
{
char* const str = "Hello, World"; //常指針,指向字符串
*str = ''M''; //可以改變字符串內容
str = "Bye, World"; //錯誤,如能改變常指針指向的內存地址
}void main(void)
{
const char* str = "Hello, World"; //指針,指向字符串常量
*str = ''M''; //錯誤,不能改變字符串內容
str = "Bye, World"; //修改指針使其指向另一個字符串
*str = ''M''; //錯誤,仍不能改變字符串內容
}
看完上面的兩個例子,是不是糊塗了?告訴你一個訣竅,在第一個例子中,const用來修 飾指針str,str不可變(也就是指向字符的常指針);第二個例子中,const用來修飾char*, 字符串char*不可變(也就是指向字符串常量的指針)。
這兩種方式可以組合起來使用,使指針和內存內容都不可變。
void main (void)
Const和引用
{
const char* const str = "Hello, World"; //指向字符串常量的常指針
*str = ''M''; //錯 誤,不能改變字符串內容
str = "Bye, World"; //錯誤,不能 改變指針指向的地址
}
引用實際上就是變量的別名,這裡 有幾條規則:
聲明變量時必須初始化
一經初始化,引用不能在指向其它變量 。
任何對引用的改變都將改變原變量。
引用和變量本身指向同一內存地址。
下面的例子演示了以上的規則:
void main(void)
用const修飾引用,使應用不可 修改,但這並不耽誤引用反映任何對變量的修改。Const加在數據類型前後均可。
{
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); //內存地址相同
r = j; //i 和 r都等於20,但r不是j的引用
r++; //i 和 r 都等於21, j 仍等於20
}
例 如:void main(void)
Const和成員函數
{
int i = 10;
int j = 100;
const int &r = i;
int const &s = j;
r = 20; //錯,不能改變內容
s = 50; //錯,不能改變內容
i = 15; // i和r 都等於15
j = 25; // j和s 都等於25
}
聲明成員函數時,末尾加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;
*str = ''M''; //錯誤,不得修改該對象
return str [pos]; //return the value at position pos
}
}
Const和 重載
重載函數的時候也可以使用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) //通過返回引用設置內存內容
{
if(pos >= 12)
return NULL;
return str[pos];
}
}
在上面的例子中,ValueAt是被重載的。Const實際上是函數參數的一部分, 在第一個成員函數中它限制這個函數不能改變對象的數據,而第二個則沒有。這個例子只是 用來說明const可以用來重載函數,沒有什麼實用意義。
實際上我們需要一個新版本的 GetValue。如果GetValue被用在operator=的右邊,它就會充當一個變量;如果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
Const的擔心
{
char *str ="Hello, World";
MyClass()
{
//void constructor
}
~MyClass()
{
//destructor
}
char& operator[](int pos) //通過返 回引用可用來更改內存內容
{
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
}
C/C++中,數據傳遞給函數的方式默 認的是值傳遞,也就是說當參數傳遞給函數時會產生一個該參數的拷貝,這樣該函數內任何 對該參數的改變都不會擴展到此函數以外。每次調用該函數都會產生一個拷貝,效率不高, 尤其是函數調用的次數很高的時候。
例如:
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沒有發生變化,因為MyFunc() 操作的是z的拷貝。為了提高效率,我們可以在傳遞參數的時候,不采用值傳遞的方式,而采 用引用傳遞。這樣傳遞給函數的是該參數的引用,而不再是該參數的拷貝。然而問題是如果 在函數內部改變了參數,這種改變會擴展到函數的外部,有可能會導致錯誤。在參數前加 const修飾保證該參數在函數內部不會被改變。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通過這種簡單 安全機制使你寫不出那種說不定是什麼時候就會掉過頭來咬你一口的代碼。你應該盡可能的 使用const引用,通過聲明你的函數參數為常變量(任何可能的地方)或者定義那種const method,你就可以非常有效確立這樣一種概念:本成員函數不會改變任何函數參數,或者不 會改變任何該對象的數據。別的程序員在使用你提供的成員函數的時候,不會擔心他們的數 據被改得一塌糊塗。