2、常量

目錄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比較

  • 在編譯階段運行使用
  • 有具體的類型,在編譯階段要進行類型檢查
  • const常量會在內存中分配內存,可是隻是進行一次分配
  • 集成化的調試工具能夠進行調試(這個不知道)
  • 能夠定義局部常量,也能夠定義全局常量(我以爲能夠用static,這個有待後面的蒐集學習)

轉別人的:

(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常量進行調試,可是不能對宏常量進行調試。
View Code

 

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成員的初始化:

  • 在類中,只有const修飾的常量,只能在構造函數的初始化列表進行初始化,類對應每一個對象的const的常量值能夠不同
  • 在類中,有const 和static組合修飾,不能在構造函數初始化列表初始化,由於不屬於對象。要在類外進行初始化,同時要加上const,不用加static。
  • 在函數內,const修飾的變量必須在聲明的時候就進行初始化,且該變量再也不從新變。
  • 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 )
...
...
  • 這是由於非const變量默認爲extern,可是const變量沒有這個默認
  • 若是要const其餘文件可以訪問,要顯示的指定爲extern。固然,在其餘地方使用的文件處,也須要加上extern,這個對於非const和const都是必須的。

 

2.五、 const引用

引用(reference)是對象的另一個名字,在對引用進行各類操做,也是對原始對象的各類操做。引用主要用做函數的行參。

引用是一種複合類型:用其餘類型定義的類型。就是不能直接用常量定義。必定要關聯到其餘的類型。

不能定義引用類型的引用,可是能夠定義任何其餘類型的引用。

引用必須用其餘類型初始化。必須初始化。

引用一旦綁定到一個對象,就不能再綁定到其餘對象。

int iValue = 10;

int &rValue = iValue;//這個就是引用

const引用是指const對象的引用。

  • 既然是指向const對象的引用,const對象不能改變,其引用也不可以被改變。

   因此const對象的引用必須知足:一、若對象爲const,則引用爲const

   const int ival = 1024;

   const int &rval = ival;//這個引用就是指向const對象的引用,不能改變,由於rval與ival相關聯,ival不能改變,其rval也不能改變,因此兩個都必須爲const。

  • 對象能夠不爲const,引用能夠爲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的對象的地址只能賦值給指向const對象的指針。若是賦值給指向非const對象的指針會出錯,由於這個指針能夠改變所指向的內容。
const double pi = 3.14;
double *ptr = π//錯誤,用指向非const對象的指針指向const對象
const double *cptr = π//這個是正確的爲指向const對象的指針指向了const對象

不能用void*保存const對象的指針,必須用const void *保存const對象的指針。

  • 能夠把非const對象的地址賦值給指向const對象的指針。
double dval = 3.14;
const double *cptr = &dval;

儘管dval不是const的,也不能經過cptr來改變dval的值,能夠經過其餘方式改變dval的值。

總結

  • 指向const對象的指針無論怎麼樣都不能改變本身所指向的對象。
  • 指向const對象的指針能夠指向const對象,也能夠指向非const對象。
  • 指向const對象的指針經常使用做函數的行參。確保傳遞給函數的實際對象在函數中不由於行參而被破壞
  • 所指向的基礎對象不能改變,可是該指針能夠改變,這個指針能夠指向多個對象。

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

總結

  • 指向const對象的指針,指針可變,對象內容不可變,無論指向的對象是否是const,都不能經過指針來修改這個對象的內容
  • const指針,指針不可變,對象則根據對象的類型判斷可變。

有一個須要注意:

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這個關鍵字。

相關文章
相關標籤/搜索