目錄html
一、#define定義常量,好與壞數組
二、const關鍵字(各類const對象,指針,引用,函數,對應的引用等等)安全
常量就是在運行期間,值一直不變。c語言用#define定義,宏常量。C++裏面用#define和const定義常量。ide
轉:http://blog.csdn.net/love_gaohz/article/details/7567856函數
http://blog.sina.com.cn/s/blog_60be7ec80100gzhm.html工具
一、define定義常量學習
定義的是全局常量spa
define宏是在預處理階段展開.net
沒有類型,僅僅是展開,不作類型展開指針
僅僅是展開,多少個地方使用,就有多少個地方展開。(宏定義不分配內存,變量定義須要分配內存)
只是替換字符串,容易產生意想不到的錯誤(邊際效益)
二、const常量
2.一、 const和define比較
轉別人的:
(1) 編譯器處理方式不一樣 define宏是在預處理階段展開。 const常量是編譯運行階段使用。 (2) 類型和安全檢查不一樣 define宏沒有類型,不作任何類型檢查,僅僅是展開。 const常量有具體的類型,在編譯階段會執行類型檢查。 (3) 存儲方式不一樣 define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。(宏定義不分配內存,變量定義分配內存。) const常量會在內存中分配(能夠是堆中也能夠是棧中)。 (4)const 能夠節省空間,避免沒必要要的內存分配。 例如: #define PI 3.14159 //常量宏 const doulbe Pi=3.14159; //此時並未將Pi放入ROM中 ...... double i=Pi; //此時爲Pi分配內存,之後再也不分配! double I=PI; //編譯期間進行宏替換,分配內存 double j=Pi; //沒有內存分配 double J=PI; //再進行宏替換,又一次分配內存! const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define同樣給出的是當即數,因此,const定義的常量在程序運行過程當中只有一份拷貝(由於是全局的只讀變量,存在靜態區),而 #define定義的常量在內存中有若干個拷貝。 (5) 提升了效率。 編譯器一般不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操做,使得它的效率也很高。 (6) 宏替換隻做替換,不作計算,不作表達式求解; 宏預編譯時就替換了,程序運行時,並不分配內存。 const 與 #define的比較 C++ 語言能夠用const來定義常量,也能夠用 #define來定義常量。可是前者比後者有更多的優勢: (1) const常量有數據類型,而宏常量沒有數據類型。編譯器能夠對前者進行類型安全檢查。而對後者只進行字符替換,沒有類型安全檢查,而且在字符替換可能會產生意料不到的錯誤(邊際效應)。 (2) 有些集成化的調試工具能夠對const常量進行調試,可是不能對宏常量進行調試。
2.二、 C++中儘可能使用const,避免使用define
對外被使用的常量應放在頭文件,內部使用的常量放在源文件的頭部。
能夠將常量都定義在一個文件裏面便於管理
2.三、類中的const成員
const定義的常量在函數執行和對象銷燬以後其空間會被釋放。
對於類中的僅僅用const修飾的常量,相對於對象來講是不變的,可是對於類來講是變的。由於一個類能夠建立不少對象,這樣在進行構造函數初始化的時候,能夠進行初始化。
因此在類聲明const成員的時候,不能就直接給const常量進行賦值。
class Test{ public: const int size = 10;//error,不能在類聲明中給const常量賦值 int array[size];//size未知
}
在類中,如何進行const賦值,只能經過構造函數的初始化列表進行賦值,並且還必須是在初始化列表裏面進行初始化。這樣的話就能夠對於每個對象有不一樣的const常量。
class Test{ public: const int size; Test(int s):size(s){ ... ... } };
那如何才能作到對於整個類來講不變的常量,可使用:枚舉,static const。
枚舉:
class Test{ public: Test():constValue(9){} enum {size1 = 100, size2 = 200};//枚舉 private: const int constValue;//這個是const常量,只能在構造函數的初始化列表裏面進行初始化。 static int staticValue;//這個是靜態變量。整個類可使用,只有一次初始化,能夠改變值 const static int allValue;// static const is ok. 這個是靜態常量 }; int Test::staticValue = 10;//不能在構造函數初始化列表初始化,不能在聲明處初始化,由於不屬於某個對象 const int Test::allValue = 10;//給靜態變量賦值的時候,不用加static,上面的staticValue也沒有加
總結:const成員的初始化:
2.四、const默認爲文件的局部變量
const默認爲文件的局部變量。
在全局做用域裏定義非const變量,它在整個程序裏面均可以進行訪問。
//file_1.cc int counter; //定義 //file_2.cc extern int counter; //能夠經過extern用counter ++counter;
可是對於在全局做用域聲明的const變量是定義爲該文件的局部變量。只存在於那個文件,不能被其餘文件訪問,若是但願其餘文件可以訪問,必須在前面加上extern,這樣,在整個程序中,均可以訪問這個const變量。
//file_1.cc //const必須在聲明的時候進行初始化(函數和全局變量) extern const int bufSize = fcn(); //file_2.cc extern const int bufSize;//能夠在這個文件使用 if (int index = 0; index != bufSize; ++ index ) ... ...
2.五、 const引用
引用(reference)是對象的另一個名字,在對引用進行各類操做,也是對原始對象的各類操做。引用主要用做函數的行參。
引用是一種複合類型:用其餘類型定義的類型。就是不能直接用常量定義。必定要關聯到其餘的類型。
不能定義引用類型的引用,可是能夠定義任何其餘類型的引用。
引用必須用其餘類型初始化。必須初始化。
引用一旦綁定到一個對象,就不能再綁定到其餘對象。
int iValue = 10;
int &rValue = iValue;//這個就是引用
const引用是指const對象的引用。
因此const對象的引用必須知足:一、若對象爲const,則引用爲const
const int ival = 1024;
const int &rval = ival;//這個引用就是指向const對象的引用,不能改變,由於rval與ival相關聯,ival不能改變,其rval也不能改變,因此兩個都必須爲const。
int i = 42;
const int &r = 42;
const int &r2 = r + i;
或者
double dval = 3.14;
const int &ri = dval;
由於編譯器會把代碼轉換爲如下形式:
int temp = dval;
const int &ri = temp;
引用綁定到了ri上面。dval的改變不會影響到ri的值。
總結:
非const引用只能綁定到與該引用同類型的對象。
const引用的賦值能夠綁定到:一、相同類型的const,非const對象。二、相關類型的const、非const對象。三、綁定到右值
利用const引用避免了賦值。
書上的話:若是使用引用行參的惟一目的是避免複製,則應定義爲const
2.6 const與指針
這個比較複雜,並且很難記;const和指針的關係有兩個:指向const對象的指針,const指針
2.6.1 指向const對象的指針
若是指針是指向const對象,則不容許用指針改變其所指的const值。
const double *cptr;//該爲指向const對象的指針
const限定的是cptr指針所指的對象,並不是cptr指針,cptr並不具備const特性,因此在定義的時候並不須要進行初始化。能夠對cptr進行從新賦值,便可以指向其餘對象。可是不能同構cptr修改所指向的內容。
*cptr = 42;//錯誤的,不能改變所指向對象的內容
++cptr;//這個是對的,由於該指針沒有const特性。
const double pi = 3.14; double *ptr = π//錯誤,用指向非const對象的指針指向const對象 const double *cptr = π//這個是正確的爲指向const對象的指針指向了const對象
不能用void*保存const對象的指針,必須用const void *保存const對象的指針。
double dval = 3.14; const double *cptr = &dval;
儘管dval不是const的,也不能經過cptr來改變dval的值,能夠經過其餘方式改變dval的值。
總結:
2.6.2 const指針
const指針:指針不能更改,可是指針所指向的對象的值能夠更改(若是對象不是const,能夠更改,若是是const,則不能更改)。即這個指針只能指向一個對象,能夠經過這個指針改變對象的值。
int errNumb = 0;
int *const curErr = &errNumb;
++curErr;//出錯,由於指針不能更改
*curErr = 1;//對的
2.6.3 這兩個很差記:
先忽略類型名,看const離哪一個近,就修飾誰;
const *p;//const修飾*p,p是指針,*p是指針指向的對象,不可變
*const p;//const修飾p,p不可變,p指向的對象可變。
const *const p;//前一個修飾*p,後一個修飾p
總結:
有一個須要注意:
string s;
typedef string *pstring;
const pstring cstr1 = &s;
pstring const cstr2 = &s;
string *const cstr3 = &s;
這三個都是同樣的,都是:指向string類型對象的const指針
2.7 const參數,函數
const參數:對於參數在函數中不須要被改變,將參數定義爲const是較好的選擇,由於這樣能夠傳遞常量進來。
int strlen(const char* str);//這個就能夠傳遞非const字符串,const字符串,以及"fjdajf"這種字符常量。若是沒有const的話,則不能傳遞最後一種狀況的字符串。前兩種恰好驗證了指向const對象的特性。
const函數:將函數定義爲const,至關於修飾返回值,不可改變
int fun(void) const;
const差很少就這些吧。若是還有,之後再加。
寫到這,忽然以爲行參能夠弄個專題:由於涉及到引用傳遞數組這個問題。還有volatile這個關鍵字。