第十一章 其他編程經驗
11、1利用const提高函數的健壯性
const更大的魅力是它可修飾函數的參數、傳回值、甚至函數的定義體。
11.1.1用const修飾函數的參數
若參數作輸出用,無論是指標傳遞還是引用傳遞,都不能加const修飾,否則該參數將失去輸出功能。const只能修飾輸入參數:
Ø
若輸入參數採用“指標傳遞”,則加const修飾可防止意外改動該指標。指標傳遞本質也是值傳遞,若使用指標常量,因調用時還要產生參數的副本,則其const將會失效,所以輸入參數是常量指標而非指標常量。const在*前為常量指標,在其後為指標常量。
Ø
若輸入參數採用“值傳遞”,因函數將自動產生臨時變數用於複製該參數,該輸入參數本來就無須保護,所以不要加const修飾。
Ø
對非內部資料類型的參數而言,像void Fun(A a)這樣的聲明註定效率較低。因函數體內將產生A類型的臨時對象用於複製參數a,而臨時對象的構造、複製、析構過程都需要時間。為提高效率,可將聲明改為void
Fun(A &a),因引用傳遞不需產生臨時對象。缺點:引用傳遞可能改變參數a,所以需改為void Fun(const A &a)。對內部資料類型,因不存在構造析構過程,所以不需改為引用傳遞。簡而言之,即對非內部資料類型,應將“值傳遞”方式改為“const引用傳遞”方式。對內部類型,使用“值傳遞”。
11.1.2用const修飾函數的傳回值
上面講了輸入參數的三種傳遞方式,函數的傳回值也有三種方式,同輸入參數的方式。
Ø
若給以“指標傳遞”方式的函數傳回值加const修飾,那麼函數傳回值的內容不能被修改,該傳回值只能賦給const修飾的同類型指標。例如:const char *GetString(void);
char *str = GetString();----------------------編譯錯誤
const char *str = GetString();----------------正確
因不可將指向常量的指標賦給非指向常量的指標。
Ø
若傳回值用“值傳遞”方式,因函數會把傳回值複製到外部臨時的儲存單元中,加const修飾無意義。對非內部資料類型,可採用“const引用傳遞”,注意引用不能指向函數的局部變數,否則出錯。
Ø
函數傳回值採用“引用傳遞”的場合不多,一般僅用於類的賦值函數中,以實現鏈式表達。
11.1.3const成員函數
任何不會修改資料成員的函數都應該聲明為const類型。若在編寫const成員函數時,不慎修改了資料成員或調用了非const成員函數,編譯將報錯。聲明格式如:
class Stack
{
public:
int Pop(void);
int GetCount(void) const;//const成員函數
private:
int m_num;
};
int Stack::GetCount(void) const
{
++m_num; //編譯錯誤
Pop(); //編譯錯誤
return m_num;
}
總結:const的用法:修飾函數函數參數、傳回值、函數體本身。
對參數和傳回值的傳遞方式,都有三種:指標傳遞、值傳遞、引用傳遞。
參數:const修飾輸出參數無意義,僅修飾輸入參數。const指標傳遞:可防止修改輸入參數;值傳遞:const修飾無意義;const引用傳遞:對非內部資料參數有用,可避免產生臨時對象,提高效率。
傳回值:const指標傳遞:傳回值內容不能被修改,只能賦給同類型的const指標;值傳遞:因要產生臨時對象,const修飾無意義;const引用傳遞:對非內部資料有用,一般僅用於類的賦值函數中。注意指標傳遞和引用傳遞,傳回值不要指向局部變數。
函數體本身:不修改資料成員就聲明為const成員函數,其內部不可改變資料成員,也不可調用非const成員函數。
區分:指向常量的指標const int *p;指標常量int *const p;。