c 中const的作用

2021-05-03 22:39:31 字數 4998 閱讀 3807

const給人的第一印象就是定義常量。

(1)const用於定義常量。

例如:const int n = 100;const int m = 200;      這樣程式中只要用到 n、m 就分別代表為整型100、200,n、m 為一常量,在程式中不可改變。      但有人說他程式設計時從來不用const定義常量。我相信。但他是不懂得真正的程式設計藝術,用const定義常量不僅能方便我們程式設計而且能提高程式的清晰性。你是願意看到程式中100、200 滿天飛,還是願意只看到簡單清晰的n、m。相信有沒有好處你慢慢體會。      還有人說他不用const定義常量,他用#define巨集定義常量。可以。但不知道你有沒有發現有時#define巨集並沒有如你所願在定義常量。下面我們比較比較const和#define。      1。      (a) const定義常量是有資料型別的:      這樣const定義的常量編譯器可以對其進行資料靜態型別安全檢查,而#define巨集定義的常量卻只是進行簡單的字元替換,沒有型別安全檢查,且有時還會產生邊際效應(不如你願處)。所謂邊際效應舉例如下:            #define n 100            #define m 200 + n            當程式中使用 m*n 時,原本想要 100 * (200+ n )的卻變成了 100 * 200 + n。      (b)#define巨集定義常量卻沒有。#define 《巨集名》《字串》,字串可以是常數、表示式、格式串等。在程式被編譯的時候,如果遇到巨集名就喲內指定的字串進行替換,然後再進行編譯。      2。      有些除錯程式可對const進行除錯,但不對#define進行除錯。      3。      當定義區域性變數時,const作用域僅限於定義區域性變數的函式體內。但用#define時其作用域不僅限於定義區域性變數的函式體內,而是從定義點到整個程 序的結束點。但也可以用#undef取消其定義從而限定其作用域範圍。只用const定義常量,並不能起到其強大的作用。const還可修飾函式形式參 數、返回值和類的成員函式等。從而提高函式的健壯性。因為const修飾的東西能受到c/c++的靜態型別安全檢查機制的強制保護,防止意外的修改。

(2)const修飾函式形式引數

形式引數有輸入形式引數和輸出形式引數。引數用於輸出時不能加const修飾,那樣會使函式失去輸出功能。因為const修飾的東西是不能改變的。      const只能用於修飾輸入引數。      談const只能用於修飾輸入引數之前先談談c++函式的三種傳遞方式。      c++函式的三種傳遞方式為:值傳遞、指標傳遞和引用傳遞。簡單舉例說明之,詳細說明請參考別的資料。   

值傳遞:        void fun(int x)      

void main(void)        

void main(void)         void main(void);       賦值 char *ch = getchar();//錯誤    const char *ch = getchar();//正確

(4)const修飾類的成員函式(函式定義體)

任何不會修改資料成員的函式都應用const修飾,這樣當不小心修改了資料成員或呼叫了非const成員函式時,編譯器都會報錯。      const修飾類的成員函式形式為:int getcount(void)  const; (5)用傳引用給const取代傳值缺 省情況下,c++ 以傳值方式將物件傳入或傳出函式(這是乙個從 c 繼承來的特性)。除非你特別指定其它方式,否則函式的引數就會以實際引數(actual argument)的拷貝進行初始化,而函式的呼叫者會收到函式返回值的乙個拷貝。這個拷貝由物件的拷貝建構函式生成。這就使得傳值(pass-by- value)成為乙個代價不菲的操作。例如,考慮下面這個類層級結構:

class person ;

class student: public person ;

現在,考慮以下**,在此我們呼叫乙個函式—— validatestudent,它得到乙個 student 引數(以傳值的方式),並返回它是否驗證有效的結果:

bool validatestudent(student s); // function taking a student // by value

student plato; // plato studied under socrates

bool platoisok = validatestudent(plato); // call the function

當這個函式被呼叫時會發生什麼呢?

很明顯,student 的拷貝建構函式被呼叫,用 plato 來初始化引數 s。同樣明顯的是,當 validatestudent 返回時,s 就會被銷毀。所以這個函式的引數傳遞代價是一次 student 的拷貝建構函式的呼叫和一次 student 的析構函式的呼叫。

但這還不是全部。乙個 student 物件內部包含兩個 string 物件,所以每次你構造乙個 student 物件的時候,你也必須構造兩個 string 物件。乙個 student 物件還要從乙個 person 物件繼承,所以每次你構造乙個 student 物件的時候,你也必須構造乙個 person 物件。乙個 person 物件內部又包含兩個額外的 string 物件,所以每個 person 的構造也承擔著另外兩個 string 的構造。最終,以傳值方式傳遞乙個 student 物件的後果就是引起一次 student 的拷貝建構函式的呼叫,一次 person 的拷貝建構函式的呼叫,以及四次 string 的拷貝構造函式呼叫。當 student 物件的拷貝被銷毀時,每乙個建構函式的呼叫都對應乙個析構函式的呼叫,所以以傳值方式傳遞乙個 student 的全部代價是六個建構函式和六個析構函式!

好了,這是正確的和值得的行為。畢竟,你希望你的全部物件都得到可靠的初始化和銷毀。儘管如此,如果有一種辦法可以繞過所有這些構造和析構過程,應該變得更好,這就是:傳引用給 const(pass by reference-to-const):

bool validatestudent(const student& s);

這樣做非常有效:沒有任何建構函式和析構函式被呼叫,因為沒有新的物件被構造。被修改的引數宣告中的 const 是非常重要的。 validatestudent 的最初版本接受乙個 student 值引數,所以呼叫者知道它們遮蔽了函式對它們傳入的 student 的任何可能的改變;validatestudent 也只能改變它的乙個拷貝。現在 student 以引用方式傳遞,同時將它宣告為 const 是必要的,否則呼叫者必然擔心 validatestudent 改變了它們傳入的 student。

以傳引用方式傳遞引數還可以避免切斷問題(slicing problem)。當乙個派生類物件作為乙個基類物件被傳遞(傳值方式),基類的拷貝建構函式被呼叫,而那些使得物件的行為像乙個派生類物件的特殊特性被 「切斷」了。你只剩下乙個純粹的基類物件——這沒什麼可吃驚的,因為是乙個基類的建構函式建立了它。這幾乎絕不是你希望的。例如,假設你在一組實現乙個圖 形視窗系統的類上工作:

class window ;

class windowwithscrollbars: public window ;

所有 window 物件都有乙個名字,你能通過 name 函式得到它,而且所有的視窗都可以顯示,你可乙個通過呼叫 display 函式來做到這一點。display 為 virtual 的事實清楚地告訴你:乙個純粹的基類的 window 物件的顯示方法有可能不同於專門的 windowwithscrollbars 物件的顯示方法。

現在,假設你想寫乙個函式列印出乙個視窗的名字,並隨後顯示這個視窗。以下這個函式的寫法是錯誤的:

void printnameanddisplay(window w) // incorrect! parameter

考慮當你用乙個 windowwithscrollbars 物件呼叫這個函式時會發生什麼:

windowwithscrollbars wwsb;

printnameanddisplay(wwsb);

引數 w 將被作為乙個 window 物件構造——它是被傳值的,記得嗎?而且使 wwsb 表現得像乙個 windowwithscrollbars 物件的特殊資訊都被切斷了。在 printnameanddisplay 中,全然不顧傳遞給函式的那個物件的型別,w 將始終表現得像乙個 window 類的物件(因為它就是乙個 window 類的物件)。特別是,在 printnameanddisplay 中呼叫 display 將總是呼叫 window::display,絕不會是 windowwithscrollbars::display。

繞過切斷問題的方法就是以傳引用給 const 的方式傳遞 w:

void printnameanddisplay(const window& w) // fine, parameter won』t

現在 w 將表現得像實際傳入的那種視窗。

內建型別很小,所以有人就斷定所有的小型別都是傳值的上等候選者,即使它們是使用者定義的。這樣的推論是不可靠的。僅僅因為乙個物件小,並不意味 著呼叫它的拷貝建構函式就是廉價的。很多物件——大多數 stl 容器也在其中——容納的和指標一樣,但是拷貝這樣的物件必須同時拷貝它們指向的每一樣東西。那可能是非常昂貴的。

即使當乙個小物件有乙個廉價的拷貝建構函式,也會存在效能問題。一些編譯器對內建型別和使用者定義型別並不一視同仁,即使他們有同樣的底層表示。 例如,一些編譯器拒絕將僅由乙個 double 組成的物件放入乙個暫存器中,即使在常規上它們非常願意將乙個純粹的 double 放入那裡。如果發生了這種事情,你以傳引用方式傳遞這樣的物件更好一些,因為編譯器理所當然會將乙個指標(引用的實現)放入暫存器。

小的使用者定義型別不一定是傳值的上等候選者的另乙個原因是:作為使用者定義型別,它的大小常常變化。乙個現在較小的型別在將來版本中可能變得更 大,因為它的內部實現可能會變化。甚至當你換了乙個不同的 c++ 實現時,事情都可能會變化。例如,就在我這樣寫的時候,一些標準庫的 string 型別的實現的大小就是另外一些實現的七倍。

通常情況下,你能合理地假設傳值廉價的型別僅有內建型別及 stl 中的迭代器和函式物件型別。對其他任何型別,請遵循本 item 的建議,並用傳引用給 const 取代傳值。

things to remember

·用傳引用給 const 取代傳值。典型情況下它更高效而且可以避免切斷問題。

·這條規則並不適用於內建型別及 stl 中的迭代器和函式物件型別。對於它們,傳值通常更合適。    只在總結,也許不夠專業,不夠全面,請大家指教。

c 中const的作用

const給人的第一印象就是定義常量。1 const用於定義常量。例如 const int n 100 const int m 200 這樣程式中只要用到 n m 就分別代表為整型100 200,n m 為一常量,在程式中不可改變。但有人說他程式設計時從來不用const定義常量。我相信。但他是不懂得...

c 中const的作用

const給人的第一印象就是定義常量。1 const用於定義常量。例如 const int n 100 const int m 200 這樣程式中只要用到 n m 就分別代表為整型100 200,n m 為一常量,在程式中不可改變。但有人說他程式設計時從來不用const定義常量。我相信。但他是不懂得...

c 中const的作用

1 const用於定義常量 例如 const int n 100 const int m 200 這樣程式中只要用到 n m 就分別代表為整型100 200,n m 為一常量,在程式中不可改變。但有人說他程式設計時從來不用const定義常量。我相信。但他是不懂得真正的程式設計藝術,用const定義常...