常量在C++裏面是一個很重要的概念,有很多細節的地方很容易忽略,這裏我根據《C++編程思想》總結了一下。
1.出現的歷史
常量最普遍的用法是值替代,在C語言裏我們用宏來定義常量:
#define MAX 100
但這是預編譯的,也就是說在編譯時只是簡單宏展開,並不檢查宏的語法是否正確。所以在C++裏引入了const來定義常量,增加了編譯時的檢查安全性得到了提高。(C99 裏面加入了const關鍵字)
const int size=100;
int array[size];
size在編譯時就知道是多少了。常量通常是保存在符號表裏面的,沒有自己的內存地址,但我們可以強制編譯器爲常量分配內存
const int i=100;
long address=(long)&i;
但要注意的是const可以用於集合,但必須保證編譯器不會複雜到把一個集合保存到它的符號表中,所以必須分配內存。在這種情況下,const意味着"不能改變的一塊存儲空間"。然而,不能在編譯期間使用它的值,因爲編譯器在編譯期間不需要知道存儲的內容。
const int i[]={1,2,3,4};
int f[i[3]]; //complie error
還有C++中默認const是內部連接的,生存期爲這個程序的運行時間。
2.指針中常量
指向const的指針:
const int* p;
int const *p;
這兩種意義一樣都是說p指向的數是個常量,但p本身可以改變。
const指針:
int* const p=&d;
p指向的變量不一定是常量,但p本身不能再指向其它的變量。
兩種可以一起使用 const int* const p=&d; 就都不能改變。
要將const看成另外一種類型,轉換時要顯示轉換。
const int e=2;
int *w=(int*)&e; //legal but bad practice
3.函數參數和返回值
void f(const int i);
這種函數就認爲在f裏面不會對 i 進行改變,調用時可以用常量,變量都行,但下面這種只能用變量做參數來調用
void f(int i);
返回const值
一般情況對於內部類型,我們都不會返回常量,但對於用戶定義的類型,按值返回常量就很重要了。如果一個函數按值返回一個類的對象爲const是,那麼這個函數的返回值不能是一個左值。
{
int i;
public:
X(int ii=0) { i=ii; }
void modify() { i++; }
};
X f5()
{
return X();
}
const X f6()
{
return X();
}
void f7(X& x)
{
x.modify();
}
int main()
{
f5()=X(1);
f5().modify; //OK
f7(f5()); //Cause warning
f6()=X(1); //compile error
f6().modify(); //error
f7(f6()); //error
}
上面例子f6返回的是常量,是不能成爲左值的。
但f5返回的不是常量也有問題,因爲返回值是個臨時量,編譯器使所有的臨時量自動地成爲const,這時編譯器必須產生一個臨時對象來保存f5的返回值,如果f7的參數是按值傳遞的話,它在f7中生成那個臨時量的副本,能很好的工作,然而f7是按引用傳遞的,這意味着它取臨時量的地址,又f7的參數不是按const引用傳遞的,可能會對參數進行修改,問題就是編譯器在計算表達式結束時,該臨時對象也會不復存在了,對臨時對象的任何修改也將丟失。其實第一行也有同樣的問題,可惜編譯器不會有提示信息。