首先,我們來看,當const關鍵字與pointer、reference變數的宣告一起使用時,會出現什麼情形:
int* ptr; // pointer本身與其指向的記憶區塊皆可覆寫。
int const* ptr; // pointer本身可覆寫,但指向的記憶區塊不可覆寫。 const int* ptr; // 同上。此為常見的寫法。
int*const ptr; // pointer本身不可覆寫,但指向的記憶區塊可覆寫。
int const*const ptr; // pointer本身與其指向的記憶區塊皆不可覆寫。
int const& ref; // reference指向的記憶區塊不可覆寫。附帶一提,reference本身一定不可覆寫。
int&const ref; // reference本身一定不可覆寫,所以沒有這種寫法。
看到這邊大家應該就了解了。在"*"的右邊有沒有const,決定這個pointer本身是不是唯讀;而"*"或"&"的左邊有沒有const,則決定這個pointer或reference所指向的記憶區塊是不是const。
以此類推,在雙層指標的宣告中使用const關鍵字,就會像這個樣子:
int const**const constPtrToPtrToConstInt; // pointer本身不可覆寫;pointer指向的記憶區塊可覆寫;pointer向的記憶區塊中存放的位址,其指向的記憶區塊不可覆寫。
int *const* ptrToConstPtrToInt; // pointer本身可覆寫;pointer指向的記憶區塊不可覆寫;pointer指向的記憶區塊中存放的位址,其指向的記憶區塊可覆寫。
在function的pointer參數中使用const關鍵字,也有相同的效果(摘自Wikipedia的const-correctness條目):
void Foo( int * ptr, int const * ptrToConst, int * const constPtr, int const * const constPtrToConst ) { *ptr = 0; // OK: modifies the pointee (可覆寫pointer指向的記憶區塊) ptr = 0; // OK: modifies the pointer (可覆寫pointer本身) *ptrToConst = 0; // Error! Cannot modify the pointee (不可覆寫pointer指向的記憶區塊) ptrToConst = 0; // OK: modifies the pointer (可覆寫pointer本身) *constPtr = 0; // OK: modifies the pointee (可覆寫pointer指向的記憶區塊) constPtr = 0; // Error! Cannot modify the pointer (不可覆寫pointer本身) *constPtrToConst = 0; // Error! Cannot modify the pointee (不可覆寫pointer指向的記憶區塊) constPtrToConst = 0; // Error! Cannot modify the pointer (不可覆寫pointer本身) }
const關鍵字除了可以用在pointer與reference的宣告,還能夠用在物件本身。
在定義與宣告class C的method時,method參數之後若沒有const關鍵字:
class C { int i; void set(int j){ i=j; } };則執行此method時,this的型態為:
C *const this;也就是說,物件本身的內容可以覆寫。
而在定義與宣告class C的method時,在method參數之後若加上const關鍵字:
class C { int i; int get() const{ return i; } };則執行此method時,this的型態會變成:
C const*const this;也就是說,物件本身的內容不可以覆寫。
以下的C++程式碼可以印證上述的特性(摘自Wikipedia的const-correctness條目):
class C { int i; public: int Get() const // Note the "const" tag { return i; } void Set(int j) // Note the lack of "const" { i = j; } }; void Foo(C& nonConstC, const C& constC) { int y = nonConstC.Get(); // Ok int x = constC.Get(); // Ok: Get() is const nonConstC.Set(10); // Ok: nonConstC is modifiable constC.Set(10); // Error! Set() is a non-const method and constC is a const-qualified object }
最近C++ template使用的場合越來越多,所以就有人寫了這樣的template function:
template<class T> void func(const T str){ str[0]='m'; printf("%s\n",str); } func<char*>(testStr);奇怪的是,編譯結果顯示,str指向的記憶區塊竟然可以覆寫!這是為什麼呢?
因為在這個場合,"const T"等同於"T const",也就是單純表示T本身不可覆寫,而不影響T所指向的記憶區塊是否可以覆寫。所以,此時str的型態其實是:
char *const str;所以說,程式能夠覆寫str指向的記憶區塊,也不是什麼奇怪的事。
若要讓str指向的記憶區塊也不可覆寫,那就要改成這樣:
func<char const*>(testStr);也因此,在實作C++ template class與function時,一般不建議把template變數宣告成pointer。而reference變數加上const時,一律表示reference指向的記憶區塊不可覆寫,自然就不會有上述的問題。所以把template變數宣告成reference,才是C++領域的權威們建議的用法。
總之,希望看過這篇的人,不要再掉入const的應用陷阱了。
沒有留言:
張貼留言