const用法總結

2021-11-07 12:20:54 字數 6351 閱讀 8222

看到const 關鍵字,很多程式設計師想到的可能是const 常量,這可有點象踩到陷井上還不知道自己危險了。讀讀以下文字會使你對c++中的const有乙個全面的認識。

const 是c++中常用的型別修飾符,有某些微妙的應用場合,如果沒有搞清本源,則錯誤在所難免。本篇中將對const進行辨析。溯其本源,究其實質,希望能對大家理解const有所幫助,根據思維的承接關係,分為如下幾個部分進行闡述。

c++中為什麼會引入const

c++的提出者當初是基於什麼樣的目的引入(或者說保留)const關鍵字呢?,這是乙個有趣又有益的話題,對理解const很有幫助。

1. 大家知道,c++有乙個型別嚴格的編譯系統,這使得c++程式的錯誤在編譯階段即可發現許多,從而使得出錯率大為減少,因此,也成為了c++與c相比,有著突出優點的乙個方面。

2. c中很常見的預處理指令 #define variablename variablevalue 可以很方便地進行值替代,這種值替代至少在三個方面優點突出:

一是避免了意義模糊的數字出現,使得程式語義流暢清晰,如下例:

#define user_num_max 107 這樣就避免了直接使用107帶來的困惑。

二是可以很方便地進行引數的調整與修改,如上例,當人數由107變為201時,進改動此處即可,

三是提高了程式的執行效率,由於使用了預編譯器進行值替代,並不需要為這些常量分配儲存空間,所以執行的效率較高。

鑑於以上的優點,這種預定義指令的使用在程式中隨處可見。

3. 說到這裡,大家可能會迷惑上述的1點、2點與const有什麼關係呢?,好,請接著向下看來:

預處理語句雖然有以上的許多優點,但它有個比較致命的缺點,即,預處理語句僅僅只是簡單值替代,缺乏型別的檢測機制。這樣預處理語句就不能享受c++嚴格型別檢查的好處,從而可能成為引發一系列錯誤的隱患。

4.好了,第一階段結論出來了:

結論: const 推出的初始目的,正是為了取代預編譯指令,消除它的缺點,同時繼承它的優點。

現在它的形式變成了:

const datatype variablename = variablevalue ;

為什麼const能很好地取代預定義語句?

const 到底有什麼大神通,使它可以振臂一揮取代預定義語句呢?

1. 首先,以const 修飾的常量值,具有不可變性,這是它能取代預定義語句的基礎。

2. 第二,很明顯,它也同樣可以避免意義模糊的數字出現,同樣可以很方便地進行引數的調整和修改。

3. 第三,c++的編譯器通常不為普通const常量分配儲存空間,而是將它們儲存在符號表中,這使得它成為乙個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高,同時,這也是它取代預定義語句的重要基礎。

這裡,我要提一下,為什麼說這一點是也是它能取代預定義語句的基礎,這是因為,編譯器不會去讀儲存的內容,如果編譯器為const分配了儲存空間,它就不能夠成為乙個編譯期間的常量了。

4. 最後,const定義也像乙個普通的變數定義一樣,它會由編譯器對它進行型別的檢測,消除了預定義語句的隱患。

const 使用情況分類詳析

用於指標的兩種情況分析:

int const *a; //a/可變,*a不可變

int *const a; //a不可變,*a可變

分析:const 是乙個左結合的型別修飾符,它與其左側的型別修飾符合為乙個型別

修飾符,所以,int const 限定 *a,不限定a。int *const 限定a,不限定*a。

限定函式的傳遞值引數:

void fun(const int var);

分析:上述寫法限定引數在函式體中不可被改變。由值傳遞的特點可知,var在函式體中的改變不會影響到函式外部。所以,此限定與函式的使用者無關,僅與函式的編寫者有關。

結論:最好在函式的內部進行限定,對外部呼叫者遮蔽,以免引起困惑。如可改寫如下:

void fun(int var)

限定函式的值型返回值:

const int fun1();

const myclass fun2();

分析:上述寫法限定函式的返回值不可被更新,當函式返回內部的型別時(如fun1),已經是乙個數值,當然不可被賦值更新,所以,此時 const無意義,最好去掉,以免困惑。當函式返回自定義的型別時(如fun2),這個型別仍然包含可以被賦值的變數成員,所以,此時有意義。

傳遞與返回位址: 此種情況最為常見,由位址變數的特點可知,適當使用const,意義昭然。

5. const 限定類的成員函式:

class classname

注意:採用此種const 後置的形式是一種規定,亦為了不引起混淆。在此函式的宣告中和定義中均要使用const,因為const已經成為型別資訊的一部分。

獲得能力:可以操作常量物件。

失去能力:不能修改類的資料成員,不能在函式中呼叫其他不是const的函式。

1.1.1. 定義普通常量

使用#define來定義常量也是常用方法,但const也可以用來定義常量,在[effective c++]中建議使用const代替#define來定義常量,因為const定義的常量具有型別資訊,而巨集沒有,所以使用const定義的常量在進行賦值操作時編譯器會進行更嚴格的型別檢查,是型別安全的。

const double pi = 3.1414926;

const int pool_size = 20;

定義常量有三種方法:巨集、const、enum,其中巨集應該盡量避免,而const與enum也各有優缺點,最大的區別就是enum只能用於定義整數,而不能定義浮點數;而對於定義邏輯關係較近的一組整數時比較適合使用enum,也可以考慮使用類代替enum.

常量必須在定義時進行初始化,之後便不能再賦值。說它不能被賦值並不是說常量的值是絕對不會改變的,只是說不能直接賦值,但可以通過指標及強制型別轉換、const_cast是可以改變常量的值的。

#include

using namespace std;

int main( void )

輸出結果:

cpci = 002dfac8, pci = 002dfac8

ci=5, *cpci=1, *pci=1

ci=5, *cpci=2, *pci=2

ci != *cpci

之所以使用ci直接輸出變數的值時顯示其值始終沒有改變,但通過指標間接顯示出來的值是改變了,而且輸出結果的最後一行很奇怪,ci的值與*cpci的值居然不相等,只因為編譯器在編譯時進行了優化,將**中的ci直接替換成了5,與巨集替換是相同的效果,而指標的值則是實際記憶體中的值。

所以,千萬不要試圖使用指標強行改變const變數的值,否則程式可能表現出錯誤的行為,而且查詢起來這種錯誤非常困難。在gcc 4.3.

4和visual c++ 2010中均預設開啟了對常量的優化選項,目前還沒找到關閉該優化的命令列選項,一定不要自作聰明去改const變數的值。

1.1.2. 修飾指標

把const與指標放到一起,很多人便會想到乙個繞口令「指標常量與常量指標。「指標常量」即乙個指標變數,該變數不能被賦值,而指標指向的記憶體單元的內容是可以改變的;「常量指標」即乙個指向常量的指標,指標變數本身可以賦值,而指標指向的記憶體單元的內容是不可以被重新賦值的。

char a = 'a', b = 'b';

const char* ptoc = &a; // 常量指標

*ptoc = 'c'; // 改變指標指向記憶體單元的內容,不可以

ptoc = &b; // 改變指標的值,可以

char* const cp = &a; // 指標常量

cp = &b; // 改變指標的值,不可以

*cp = 'd'; // 改變指標指向記憶體單元的內容,可以

const char* const cptoc = &a; // 指向常量的指標常量

*cptoc = 'e'; // 不可以

cptoc = &b; // 不可以

const是修飾型別還是修飾指標,要看const的位置,放在*前就是修飾資料型別,放到*後就是修飾指標,const char和char const是一樣的。

建議:在不打算修改資料內容的時候都將指標定義成常量指標,不打算指標本身被修改的場合都定義成指標常量。盡可能地多用const,用錯了沒關係,編譯器會提示你的,只要能夠編譯通過,就不會因為用錯const而導致程式邏輯錯誤,應該說const負作用極小。

1.1.3. 修飾類成員常量

當使用const修飾類成員變數時便定義了常資料成員,它的使用與使用類外定義的常量本質上並沒有什麼區別,在這裡只想指出一點:有網友提到const資料成員只能被const修飾的函式使用這是沒有根據的,是錯誤的。

1.1.4. 修飾類成員函式

const修飾成員函式語法:

class socket

private:

socket_port_t _port;

mutable int _readcount;

};使用const修飾的成員函式不能修改類的成員變數,如成員_port,而且只能呼叫成員類物件const函式,但有個例外,就是mutable修飾的成員變數可以在const修飾的成員函式中被修改,如_readcount。

另外,const只能修飾非靜態函式。

建議:將所有不改變物件狀態的函式都使用const修飾符標識,以提高程式的可讀性。其實,標頭檔案就是最好的類介面的說明文件,越多的提供資訊就能使程式的可讀性越好,越利於維護。

看到成員函式的const修飾符,讀者便立即明白該函式不會改變程式的狀態,這也有利於當程式狀態出現異常時的問題定位。

1.1.5. 修飾類物件、物件引用或物件指標(常量指標)

當const修飾自定義的類物件時,與修飾c++內建型別的變數的思想是一致的,但稍有不同,除了不能被賦值外,還不能呼叫沒有使用const修飾的非靜態成員函式。當const修飾類物件引用、指標時限制是一樣的,因為引用本身與直接使用該變數實質上沒有區別,而使用指標只是將.操作符改為了->本質上還是一樣的

const std::string hello = 「hello from noock tian;

std::cout<

hello = "hi"; // 不可以賦值

hello.push_back不可以

1.1.6. 修飾函式引數

const修飾函式的例子是很常見了,表示函式的引數在函式體內不會被意外修改,一般用於修飾輸入引數,例如標準庫中的字串連線函式。str1是輸出引數,其內容會被修改,而str2為輸入引數,其內容不會修改。

char* strcat(char* str1, const char* str2);

實際上在說到const用法一開始就提到,const只是一種宣告,但並不能保證,例如strcat函式雖然宣告了str2為const char*型,但並不能保證內部絕對不會修改str2的內容。但const從語言本身提供了一種編寫自描述性**的方法,只要使用函式與實現函式的雙方都達成一致的約定,按照契約程式設計,我們就可以認為const修飾的型別在函式體內不會被修改,這與const修飾類成員函式一樣,可以提高軟體的可讀性。

1.1.7. 修飾函式返回值

const可以用於修飾任何型別,只要返回值型別不是void,const就可以用來修飾返回值的型別。但實際上const用於修飾非引用的返回值型別是沒有意義的,因為返回值一般都會被賦值給另乙個變數,此時用於傳遞返回值的物件已經被銷毀,修飾返回值型別的const的作用也就終結了。

當返回值是引用型別時,如果該引用的值不希望被修改是可以宣告為常引用的,例如:

class socket

private:

string _ip;

};socket sock;

string& ip = sock.ip(); // 不可以

const string& ip2 = sock.ip(); // 可以

string ip3 = sock.ip(); // socket::_ip被複製,可以

此處,為了減少構造臨時變數,將ip函式返回值定義為引用型別以提高程式執行效率,但為了保護內部狀態不會被客戶端**意外,返回值使用const修飾為常引用。但是,如果對於軟體安全性較高的場合,最好不要定義為引用,因為惡意的客戶端**是有可能修改socket::_ip的值的。

在c++中賦值運算子反預設返回值都是引用,但筆者認為定義為常引用更為合適,例如:

int main( void )

輸出結果:

a=4, b=2, c=3, d=4

顯然,在實際工程中誰也不會寫出這樣的**,這段**卻是合法的,無疑這給程式設計師多了一種出錯的可能,如果把賦值運算子的返回值定義為常引用,則會減少程式設計師出錯的機會,例如[??]:

class object

};int main( void )

在gcc 編譯時則會出現錯誤提示:

error: passing 『const object』 as 『this』 argument of 『const object& object::operator=(const object&)』 discards qualifiers.

當然,不同的編譯器可能錯誤提示不同。

const用法總結 Cby K

int i 10 const int ref 42 const int ref1 r i double d 3.14 const int ref2 d 以繫結到不同型別的ref2為例解釋原因,編譯器會把ref2相關的 轉換如下 int temp d const int ref2 temp bind ...

Const用法小結

常量,如const int max 100 優點 const常量有資料型別,而巨集常量沒有資料型別。編譯器可以對前者進行型別安全檢查,而對後者只進行字元替換,沒有型別安全檢查,並且在字元替換時可能會產生意料不到的錯誤 邊際效應 修飾類的資料成員。如 class a const資料成員只在某個物件生存...

C 中const用法總結

c 中const用法總結 月光林地 部落格園 修飾普通變數和指標 const修飾變數,一般有兩種寫法 consttypevalue typeconstvalue 這兩種寫法在本質上是一樣的。它的含義是 const修飾的型別為type的變數value是不可變的。對於乙個非指標的型別type,無論怎麼寫...