編程中是否遇到這樣情況?
(基本內容來源於C++ Prime)
1、用一個變量表示緩沖區的大小。當我們覺得不合適時,直接改變變量值即可,可以很方便調整緩沖區大小
2、但要警惕程序不小心改變了這個值
OK,遇到這樣情況,使用const修飾符即可。
言簡意賅,總結一下。
首先上目錄:
1、const對象必須初始化。
2、默認情況下,cosnt對象只在文件內有效
3、const的引用
4、const與指針
5、頂層const 和 底層const
6、C++常量折疊
7、const函數
8、const成員變量
1、const對象必須初始化。
因為const對象一旦創建,就不能改變了。
2、默認情況下,cosnt對象只在文件內有效
下面論述一下const對象和普通對象的區別。
(轉載於http://blog.csdn.net/yipiantiandi/article/details/5822564)
(const對象默認作用域為:文件作用域)
(普通變量默認作用域:整個程序)
對於一般的對象 如 int a=9;;我們知道它的作用域是整個程序的,在1.cpp和2.cpp中包含同一個定義了int a=9;的頭文件,因為int a=9;作用域是整個程序,所以會產生錯誤。
那為什么const int a=9;不會產生錯誤呢。原因就是const int a=9;的默認作用范圍是文件作用域的。即,盡管在1.cpp和2.cpp中包含同一個定義了const int a=9;的頭文件,但由於const int a=9;是文件作用域的,包含了頭文件的1.cpp和2.cpp盡管都包含了const int a=9;但他們是各自文件內的const對象,兩者互不相干,就像不同函數中定義了相同的變量名一樣。
所以,通過在頭文件中定義const對象,可以實現多個文件使用相同的常量值,這在實際的程序中很常見。
那么如何使用其他文件中的const
說到這里,有一點疑問需要解決:如果想在別的文件中訪問本文件中的const對象該怎么辦,如果都是單是在別的文件中使用extern const int a = 9;的話,不會起任何作用,因為已經提示const int a = 9;是文件作用域的。
所以應該在需要被其他文件使用的const對象定義成這樣:extern const int a = 9;而在別的需要使用這個const對象的地方聲明:extern const int a;前者是定義,后者是聲明。這樣就可以使用了。
3、const的引用
| 引用前帶const修飾符 | 被引用前帶const修飾符 | 附注 |
| 是 | 是 | 對const變量的const引用 |
| 是 | 否 | 引用認為它引用的變量不可以改變,但實際可以(代碼附注1) |
| 否 | 是 | 不可以(代碼附注2) |
| 否 | 否 | 就是普通的引用 |
代碼附注1:
int i=42; const int &r_i = i; cout<<"r_i: "<<r_i<<endl; cout<<"i: "<<i<<endl; i=41;//可以 cout<<"r_i: "<<r_i<<endl; cout<<"i: "<<i<<endl; r_i = 40;//報錯 cout<<"r_i: "<<r_i<<endl; cout<<"i: "<<i<<endl;
detail it:對const的引用可能是引用一個並非const的對象
常量引用僅對引用可以參與的操作做出了限定,對於引用的對象本身是不是常量未做限定。因為對象也可能是一個非常量,所以允許通過其他途徑改變它的值。
代碼附注2
const int r1=42; int &r2 = r1;//試圖讓一個非常量引用指向一個常量對象。如果該初始化合法,則可以通過r2來改變它引用對象的值,這顯然不正確。
4、const與指針
| 指針 | 被指數據 | 附注 |
| 變量 | 變量 | 普通的指針 |
| 變量 | 常量 | 不可以 |
| 常量 | 變量 | 可以。常量指針“自以為是”,認為自己指向了常量,所以自覺的不去改變所指對象的值。實際上,這個值是可以改變的 |
| 常量 | 常量 | 可以。 |
| Pointer to Constant (常量指針/常指針) | Pointer Constant(指針常量) |
| const int*p1; const int x = 1; p1 = &x; |
int*const p2 = &x; |
| p1是一個指針 它是一個常量 |
p2是一個指針 它指向一個常量 |
| 准則:在前先讀,在前不變 | const int *x | int* const y |
| * (指針)和const(常量)誰在前先讀誰; * 代表被指的數據,名字代表指針地址 const在誰前面誰就不允許改變。 |
const在*前面,該數據不能改變 | const在地址前面,該地址不能變化 |
(此小節內容來自網易公開課,北京郵電大學,崔毅東老師的《C++程序設計入門(上)》)
5、由上一節的內容,可以引申出一個概念:
頂層const 和 底層const
該對象是const,說明其為頂層const
該對象指向或者引用的變量是const,說明其為底層const
const int ci = 42; // 不能改變 ci 的值,這是一個頂層 const
const int *p2 = &ci; // 允許改變 p2 的值,這是一個底層 const
const int *const p3 = p2; // 靠右的 const 是頂層 const ,靠左的是底層 const
6、C++常量折疊
常量與宏定義一直被面試官津津樂道
轉載於http://blog.csdn.net/yby4769250/article/details/7359278
看個code先,
#define PI 3.14 int main() { const int r = 10; int p = pI; //這里會在預編譯階段產生宏替換,PI直接替換為3.14,其實就是int p = 3.14; int len = 2*r; //這里會發生常量折疊,也就是對常量r的引用會替換成他對應的值,相當於int len = 2*10; return 0; }
如上述代碼中所述,常量折疊表面上的效果和宏替換是一樣的,只是,“效果上是一樣的”,
而兩者真正的區別在於,
1、宏是字符常量,在預編譯完宏替換完成后,該宏名字會消失,所有對宏如PI的引用已經全部被替換為它所對應的值,編譯器當然沒有必要再維護這個符號。
2、而常量折疊發生的情況是,對常量的引用全部替換為該常量如r的值,但是,常量名r並不會消失,編譯器會把他放入到符號表中,同時,會為該變量分配空間,棧空間或者全局空間
為了能更清楚的體現出常量折疊,下面做幾個對照實驗,看代碼和輸出便了然:
int main() { int i0 = 11; const int i=0; //定義常量i int *j = (int *) &i; //看到這里能對i進行取值,判斷i必然后自己的內存空間
//請注意,int *j = &i;是不對的哦,我們在第4節講過~
*j=1; //對j指向的內存進行修改
printf("%d\n%d\n%d\n%d\n",&i,j,i,*j); //觀看實驗效果 const int ck = 9; //這個對照實驗是為了觀察,對常量ck的引用時,會產生的效果 int ik = ck; int i1 = 5; //這個對照實驗是為了區別,對常量和變量的引用有什么區別 int i2 = i1; return 0; }
上面的代碼會輸出:
0012ff7c
0012ff7c
0
1
這能說明什么,至少能說明兩點:
1、i和j地址相同,指向同一塊空間,i雖然是可折疊常量,但是,i確實有自己的空間
2、i和j指向同一塊內存,但是*j = 1對內存進行修改后,按道理來說,*j==1,i也應該等於1,而實驗結果確實i實實在在的等於0,這是為什么呢,就是本文所說的內容,i是可折疊常量,在編譯階段對i的引用已經別替換為i的值了,也就是說
printf("%d\n%d\n%d\n%d\n",&i,j,i,*j)
中的i已經被替換,其實已經被改為
printf("%d\n%d\n%d\n%d\n",&i,j,0,*j)
為了使實驗更具說服力,直接上匯編代碼,比較實驗的不同點:
4: int main() 5: { 00401030 push ebp 00401031 mov ebp,esp 00401033 sub esp,5Ch 00401036 push ebx 00401037 push esi 00401038 push edi 00401039 lea edi,[ebp-5Ch] 0040103C mov ecx,17h 00401041 mov eax,0CCCCCCCCh 00401046 rep stos dword ptr [edi] 6: int i0 = 11; 00401048 mov dword ptr [ebp-4],0Bh 7: 8: const int i=0; 0040104F mov dword ptr [ebp-8],0 //睜大眼睛,編譯器確實為常量i分配了棧空間,並賦值為0 9: int *j = (int *) &i; 00401056 lea eax,[ebp-8] 00401059 mov dword ptr [ebp-0Ch],eax 10: *j=1; 0040105C mov ecx,dword ptr [ebp-0Ch] 0040105F mov dword ptr [ecx],1 11: //再看看下面的對比實驗,看出對常量的引用和變量的引用的區別 12: const int ck = 9; 00401065 mov dword ptr [ebp-10h],9 //為常量分配棧空間 13: int ik = ck; 0040106C mov dword ptr [ebp-14h],9 //看到否,對常量ck的引用,會直接替換為常量的值9,再看下面的實驗 14: 15: int i1 = 5; 00401073 mov dword ptr [ebp-18h],5 16: int i2 = i1; //這里引用變量i1,對i2進行賦值,然后看到否,對常量i1引用沒有替換成i1的值,而是去棧中先取出i1的值,到edx寄存器中,然后再把值mov到i2所在的內存中 0040107A mov edx,dword ptr [ebp-18h] 0040107D mov dword ptr [ebp-1Ch],edx 17: 18: 19: return 0; 00401080 xor eax,eax 20: 21: }
總結:常量折疊說的是,在編譯階段,對該變量進行值替換,同時,該常量擁有自己的內存空間,並非像宏定義一樣不分配空間,需澄清這點
7、const修飾函數
const 放在函數后表示這個函數是常成員函數, 常成員函數是不能改變成員變量值的函數。
const 限定符,它把一個對象轉換成一個常量。
1、非常量成員函數不能被常量成員對象調用,因為它可能企圖修改常量的數據成員:
2、非常量成員函數不能被常量實例調用,因為它可能企圖修改常量的數據成員:
為了使成員函數的意義更加清楚,我們可在不改變對象的成員函數的函數原型中加上const說明:
class Point
{
public:
int GetX() const;
int GetY() const;
void SetPt (int, int);
void OffsetPt (int, int);
private:
int xVal, yVal;
};
const成員函數應該在函數原型說明和函數定義中都增加const限定:
int Point::GetY() const
{
return yVal;
}
class Set {
public:
Set (void){ card = 0; }
bool Member(const int) const;
void AddElem(const int);
};
bool Set::Member (const int elem) const
{
}
非常量成員函數不能被常量成員對象調用,因為它可能企圖修改常量的數據成員:
const Set s;
s.AddElem(10); // 非法: AddElem不是常量成員函數
s.Member(10); // 正確
#include <iostream> using namespace std; class A { public: A(int size) : SIZE(size) {}; private: const int SIZE; }; int main() { A a(100); }
- 在類中聲明變量為const類型,但是不可以初始化
- const常量的初始化必須在構造函數初始化列表中初始化,而不可以在構造函數函數體內初始化
此時的const變量屬於具體的一個對象,如何在整個類中都恆定不變呢?
答案是利用枚舉,舉例
#include <iostream> using namespace std; class A { private: enum {SIZE = 100}; public: int array[SIZE]; }; int main() { A a; }
枚舉常量不會占據對象的存儲空間,在編譯時被全部求值
但是,它隱含的數據對象類型為整形,不能表示其他類型。
