如果在建立 string
實例時指定 const
,那表示不能變動該實例的狀態,如果試圖改變該實例狀態,或者呼叫了會變動實例狀態的方法,編譯時會發生錯誤:
const string text = "Justin";
text.append(" Lin") // error: no matching function
const
修飾表示不能變動實例狀態,這並不是自動發生的事情,如果呼叫的方法沒有被 const
修飾,就不能通過編譯,例如,若如下使用〈定義類別〉中的 Account
,雖然 to_string
並沒有變動實例狀態,也不能通過編譯:
#include <iostream>
#include "account.h"
using namespace std;
int main() {
const Account acct = {"123-456-789", "Justin Lin", 1000};
cout << acct.to_string() << endl; // error: passing 'const Account' as 'this' argument discards qualifiers
}
如果要通過編譯的話,to_string
必須加上 const
限定:
account.h
#include <string>
using namespace std;
class Account {
private:
string id;
string name;
double balance;
public:
Account(string id, string name, double balance);
void deposit(double amount);
void withdraw(double amount);
string to_string() const;
};
account.cpp
...略
string Account::to_string() const {
return string("Account(") +
this->id + ", " +
this->name + ", " +
std::to_string(this->balance) + ")";
}
...略
當方法被加上 const
限定後,方法就不能有改變值域的動作,有了這個保證,方才的 to_string
呼叫才能通過編譯。
另一個類似的問題是:
#include <iostream>
#include "account.h"
#include <string>
using namespace std;
class Foo {
public:
Foo& doSome() {
return *this;
}
Foo& doOther() {
return *this;
}
};
int main() {
const Foo foo;
foo.doSome().doOther();
}
這個程式在 Foo foo
前,若沒有加上 const
的話,是可以通過編譯的,你可能會想,那就在 doSome
、doOther
函式本體前,也加上 const
就可以了吧!可惜…加上了還是不能通過編譯!
const
的要求很嚴格,不僅要求方法不能變動實例狀態,如果以參考傳回型值域,或者是如上以參考傳回實例本身,也會要求傳回值的狀態不得改變,必須得如下才能通過編譯:
#include <iostream>
#include "account.h"
#include <string>
using namespace std;
class Foo {
public:
const Foo& doSome() const {
return *this;
}
const Foo& doOther() const {
return *this;
}
};
int main() {
Foo foo;
foo.doSome().doOther();
}
你可能會想在被限定為 const
的方法中,可以改變某些值域,因為這些值域的改變,從使用者來看,並不代表實例狀態的改變。
這是什麼意思呢?例如,若有個 hashCode
方法,可以計算出物件的雜湊值,實際上可以在首次呼叫該方法計算出雜湊值之後,使用值域將雜湊儲存下來,物件的 hashCode
結果從使用者來看並沒有變,若是有這類需求,值域在宣告時,可以加上 mutable
:
class Foo {
mutable int hash = 0;
public:
int hashCode() const {
if(hash == 0) {
// 計算雜湊值 v
this->hash = v;
}
return this->hash;
}
};