久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2747|回復: 0
打印 上一主題 下一主題
收起左側

C語言CONST的使用

[復制鏈接]
跳轉到指定樓層
樓主
ID:99624 發表于 2015-12-27 05:45 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
1、什么是const?

常類型是指使用類型修飾符const說明的類型,常類型的變量或對象的值是不能被更新的。(當然,我們可以偷梁換柱進行更新:)

2、為什么引入const?

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

3、cons有什么主要的作用?

(1)可以定義const常量,具有不可變性。

例如:

const int Max="100";

int Array[Max];

(2)便于進行類型檢查,使編譯器對處理內容有更多了解,消除了一些隱患。

例如:

void f(const int i) { .........}

編譯器就會知道i是一個常量,不允許修改;

(3)可以避免意義模糊的數字出現,同樣可以很方便地進行參數的調整和修改。

同宏定義一樣,可以做到不變則已,一變都變!如(1)中,如果想修改Max的內容,只需要:const int Max="you" want;即可!

(4)可以保護被修飾的東西,防止意外的修改,增強程序的健壯性。

還是上面的例子,如果在函數體內修改了i,編譯器就會報錯;

例如:

void f(const int i) { i="10";//error! }

(5) 為函數重載提供了一個參考。

class A

{

......

void f(int i) {......} //一個函數

void f(int i) const {......} //上一個函數的重載

......

};

(6) 可以節省空間,避免不必要的內存分配。

例如:

#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定義的常量在內存中有若干個拷貝。

(7) 提高了效率。

編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內存的操作,

使得它的效率也很高。

3、如何使用const?

(1)修飾一般常量

   一般常量是指簡單類型的常量。這種常量在定義時,修飾符const可以用在類型說明符前,也可以用在類型說明符后。

例如:

int const x="2";  或  const int x="2";

(2)修飾常數組

   定義或說明一個常數組可采用如下格式:

   int const a[5]={1, 2, 3, 4, 5}; 

const int a[5]={1, 2, 3, 4, 5};

(3)修飾常對象

 常對象是指對象常量,定義格式如下:

class A;

   const A a;

A const a;

   定義常對象時,同樣要進行初始化,并且該對象不能再被更新,修飾符const可以放在類名后面,也可以放在類名前面。 

(4)修飾常指針

const int *A;    //const/修飾指向的對象,A可變,A指向的對象不可變

int const *A;   //const修飾指向的對象,A可變,A指向的對象不可變

int *const A;    //const修飾指針A, A不可變,A指向的對象可變

const int *const A; //指針A和A指向的對象都不可變

(5)修飾常引用

 使用const修飾符也可以說明引用,被說明的引用為常引用,該引用所引用的對象不能被更新。其定義格式如下:

   const double & v;

  (6)修飾函數的常參數

const修飾符也可以修飾函數的傳遞參數,格式如下:

void Fun(const int Var);

告訴編譯器Var在函數體中的無法改變,從而防止了使用者的一些無意的或錯誤的修改。

(7)修飾函數的返回值:

const修飾符也可以修飾函數的返回值,是返回值不可被改變,格式如下:

const int Fun1();

const MyClass Fun2();

(8)修飾類的成員函數:

const修飾符也可以修飾類的成員函數,格式如下:

class ClassName

{

public:

   int Fun() const;

  .....

};

這樣,在調用函數Fun時就不能修改類里面的數據

(9)在另一連接文件中引用const常量

extern const int i; //正確的引用

extern const int j="10"; //錯誤!常量不可以被再次賦值

另外,還要注意,常量必須初始化!

例如:

const int i="5";

4、幾點值得討論的地方:

(1)const究竟意味著什么?

說了這么多,你認為const意味著什么?一種修飾符?接口抽象?一種新類型?

也許都是,在Stroustup最初引入這個關鍵字時,只是為對象放入ROM做出了一種可能,對于const對象,C++既允許對其進行靜態初始化,也允

許對他進行動態初始化。理想的const對象應該在其構造函數完成之前都是可寫的,在析夠函數執行開始后也都是可寫的,換句話說,const對

象具有從構造函數完成到析夠函數執行之前的不變性,如果違反了這條規則,結果都是未定義的!雖然我們把const放入ROM中,但這并不能夠

保證const的任何形式的墮落,我們后面會給出具體的辦法。無論const對象被放入ROM中,還是通過存儲保護機制加以保護,都只能保證,對于

用戶而言這個對象沒有改變。

(2)位元const V.S. 抽象const?

對于關鍵字const的解釋有好幾種方式,最常見的就是位元const 和 抽象const。下面我們看一個例子:

class A

{

public:

......

A f(const A& a);

......

};

如果采用抽象const進行解釋,那就是f函數不會去改變所引用對象的抽象值,如果采用位元const進行解釋,那就成了f函數不會去改變所引用

對象的任何位元。

我們可以看到位元解釋正是c++對const問題的定義,const成員函數不被允許修改它所在對象的任何一個數據成員。

為什么這樣呢?因為使用位元const有2個好處:

最大的好處是可以很容易地檢測到違反位元const規定的事件:編譯器只用去尋找有沒有對數據成員的賦值就可以了。另外,如果我們采用了位

元const,那么,對于一些比較簡單的const對象,我們就可以把它安全的放入ROM中,對于一些程序而言,這無疑是一個很重要的優化方式。

當然,位元const也有缺點,要不然,抽象const也就沒有產生的必要了。

首先,位元const的抽象性比抽象const的級別更低!實際上,大家都知道,一個庫接口的抽象性級別越低,使用這個庫就越困難。

其次,使用位元const的庫接口會暴露庫的一些實現細節,而這往往會帶來一些負面效應。所以,在庫接口和程序實現細節上,我們都應該采用

抽象const。

有時,我們可能希望對const做出一些其它的解釋,那么,就要注意了,目前,大多數對const的解釋都是類型不安全的,這里我們就不舉例子

了,你可以自己考慮一下,總之,我們盡量避免對const的重新解釋。

(3)放在類內部的常量有什么限制?

看看下面這個例子:

class A

{

private:

const int c3 = 7; // ???

static int c4 = 7; // ???

static const float c5 = 7; // ???

......

};

你認為上面的3句對嗎?呵呵,都不對!使用這種類內部的初始化語法的時候,常量必須是被一個常量表達式初始化的整型或枚舉類型,而且必

須是static和const形式。這顯然是一個很嚴重的限制!

那么,我們的標準委員會為什么做這樣的規定呢?一般來說,類在一個頭文件中被聲明,而頭文件被包含到許多互相調用的單元去。但是,為

了避免復雜的編譯器規則,C++要求每一個對象只有一個單獨的定義。如果C++允許在類內部定義一個和對象一樣占據內存的實體的話,這種規

則就被破壞了。

(4)如何初始化類內部的常量?

一種方法就是static 和 const 并用,在內部初始化,如上面的例子;

另一個很常見的方法就是初始化列表:

class A

{

public:

A(int i="0"):test(i) {}

private:

const int test;

};

還有一種方式就是在外部初始化,例如:

class A

{

public:

A() {}

private:

static const int i; //注意必須是靜態的!

};

const int A::i=3;

(5)常量與數組的組合有什么特殊嗎?

我們給出下面的代碼:

const int size[3]={10,20,50};

int array];

有什么問題嗎?對了,編譯通不過!為什么呢?

const可以用于集合,但編譯器不能把一個集合存放在它的符號表里,所以必須分配內存。在這種情況下,const意味著“不能改變的一塊存儲

”。然而,其值在編譯時不能被使用,因為編譯器在編譯時不需要知道存儲的內容。自然,作為數組的大小就不行了:)

(6)this指針是不是const類型的?

this指針是一個很重要的概念,那該如何理解她呢?也許這個話題太大了,那我們縮小一些:this指針是個什么類型的?這要看具體情況:如

果在非const成員函數中,this指針只是一個類類型的;如果在const成員函數中,this指針是一個const類類型的;如果在volatile成員函數中

,this指針就是一個volatile類類型的。

(7)const到底是不是一個重載的參考對象?

先看一下下面的例子:

class A

{

......

void f(int i) {......} //一個函數

void f(int i) const {......} //上一個函數的重載

......

};

上面是重載是沒有問題的了,那么下面的呢?

class A

{

......

void f(int i) {......} //一個函數

void f(const int i) {......} //?????/

......

};

這個是錯誤的,編譯通不過。那么是不是說明內部參數的const不予重載呢?再看下面的例子:

class A

{

......

void f(int& ) {......} //一個函數

void f(const int& ) {......}//?????/

......

};

這個程序是正確的,看來上面的結論是錯誤的。為什么會這樣呢?這要涉及到接口的透明度問題。按值傳遞時,對用戶而言,這是透明的,用

戶不知道函數對形參做了什么手腳,在這種情況下進行重載是沒有意義的,所以規定不能重載!當指針或引用被引入時,用戶就會對函數的操

作有了一定的了解,不再是透明的了,這時重載是有意義的,所以規定可以重載。

(8)什么情況下為const分配內存?

以下是我想到的可能情況,當然,有的編譯器進行了優化,可能不分配內存。

A、作為非靜態的類成員時;

B、用于集合時;

C、被取地址時;

D、在main函數體內部通過函數來獲得值時;

E、const的 class或struct有用戶定義的構造函數、析構函數或基類時;。

F、當const的長度比計算機字長還長時;

G、參數中的const;

H、使用了extern時。

不知道還有沒有其他情況,歡迎高手指點:)

(9)臨時變量到底是不是常量?

很多情況下,編譯器必須建立臨時對象。像其他任何對象一樣,它們需要存儲空間而且必須被構造和刪除。區別是我們從來看不到編譯器負責

決定它們的去留以及它們存在的細節。對于C++標準草案而言:臨時對象自動地成為常量。因為我們通常接觸不到臨時對象,不能使用與之相關

的信息,所以告訴臨時對象做一些改變有可能會出錯。當然,這與編譯器有關,例如:vc6、vc7都對此作了擴展,所以,用臨時對象做左值,

編譯器并沒有報錯。

(10)與static搭配會不會有問題?

假設有一個類:

class A

{

public:

......

static void f() const { ......}

......

};

我們發現編譯器會報錯,因為在這種情況下static不能夠與const共存!

為什么呢?因為static沒有this指針,但是const修飾this指針,所以...

(11)如何修改常量?

有時候我們卻不得不對類內的數據進行修改,但是我們的接口卻被聲明了const,那該怎么處理呢?我對這個問題的看法如下:

1)標準用法:mutable

class A

{

public:

A(int i="0"):test(i) { }

void Setvalue(int i)const { test="i"; }

private:

mutable int test;//這里處理!

};

2)強制轉換:const_cast

class A

{

public:

A(int i="0"):test(i) { }

void Setvalue(int i)const

{ const_cast (test)=i; }//這里處理!

private:

int test;

};

3)靈活的指針:int*

class A

{

public:

A(int i="0"):test(i) { }

void Setvalue(int i)const

{ *test=i; }

private:

int* test; //這里處理!

};

4)未定義的處理

class A

{

public:

A(int i="0"):test(i) { }

void Setvalue(int i)const

{ int *p=(int*)&test; *p=i; }//這里處理!

private:

int test;

};

注意,這里雖然說可以這樣修改,但結果是未定義的,避免使用!

5)內部處理:this指針

class A

{

public:

A(int i="0"):test(i) { }

void Setvalue(int i)const

{ ((A*)this)->test=i; }//這里處理!

private:

int test;

};

6)最另類的處理:空間布局

class A

{

public:

A(int i="0"):test(i),c('a') { }

private:

char c;

const int test;

};

int main()

{

A a(3);

A* pa=&a;

char* p=(char*)pa;

int* pi=(int*)(p+4);//利用邊緣調整

*pi=5; file://此處改變了test的值!

return 0;

}

雖然我給出了6中方法,但是我只是想說明如何更改,但出了第一種用法之外,另外5種用法,我們并不提倡,

(12)最后我們來討論一下常量對象的動態創建。

既然編譯器可以動態初始化常量,就自然可以動態創建,例如:

const int* pi="new" const int(10);

這里要注意2點:

1)const對象必須被初始化!所以(10)是不能夠少的。

2)new返回的指針必須是const類型的。

那么我們可不可以動態創建一個數組呢?

答案是否定的,因為new內置類型的數組,不能被初始化。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: re久久 | 久久久久综合 | 人人九九精 | 美日韩免费视频 | 亚洲小视频 | 色就干 | 我爱操 | 亚洲一区在线免费观看 | 国产精品久久久久久久久免费桃花 | 播放一级毛片 | 人人澡人人射 | 国产激情在线 | 精品乱码一区二区三四区视频 | 欧美在线播放一区 | 国产伦一区二区三区视频 | 99re6在线视频 | 日本aaaa | 亚洲一区二区精品视频在线观看 | 国产精品特级毛片一区二区三区 | 九九九视频精品 | 在线观看亚洲欧美 | 亚洲一区二区三区视频在线 | 最新国产视频 | 综合久 | 三级视频在线观看电影 | av一区在线 | 岛国毛片在线观看 | 亚洲精品欧美一区二区三区 | 亚洲大片在线观看 | 成人免费视频 | 午夜天堂精品久久久久 | 国产亚洲精品精品国产亚洲综合 | 亚洲精品在线91 | 亚洲精品国产成人 | 精品国产乱码久久久 | 黄色免费看 | 欧美精品综合在线 | 波多野结衣一区二区 | 成人在线电影在线观看 | 亚洲视频区 | 中文字幕一区二区三区四区五区 |