轉:C++中const、volatile、mutable的用法

const修飾普通變量和指針

const修飾變量,一般有兩種寫法:

const TYPE value;

TYPE const value;

這兩種寫法在本質上是一樣的。它的含義是:const修飾的類型為TYPE的變量value是不可變的。對于一個非指針的類型TYPE,無論怎么寫,都是一個含義,即value值不可變。?例如:

const int nValue??? //nValueconst

int const nValue??? //nValueconst

但是對于指針類型的TYPE,不同的寫法會有不同情況:

(1)?指針本身是常量不可變

(char*) const pContent;

(2)指針所指向的內容是常量不可變

const (char) *pContent;

(char) const *pContent;

(3)?兩者都不可變

const char* const pContent;

識別const到底是修飾指針還是指針所指的對象,還有一個較為簡便的方法,也就是沿著*號劃一條線:

如果const位于*的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;

如果const位于*的右側,const就是修飾指針本身,即指針本身是常量。

const修飾函數參數

const修飾函數參數是它最廣泛的一種用途,它表示在函數體中不能修改參數的值(包括參數本身的值或者參數其中包含的值)

void function(const int Var); ????//傳遞過來的參數在函數內不可以改變(無意義,該函數以傳值的方式調用)

void function(const char* Var); ??//參數指針所指內容為常量不可變

void function(char* const Var); ??//參數指針本身為常量不可變(也無意義,var本身也是通過傳值的形式賦值的)

void function(const Class& Var); //引用參數在函數內不可以改變

參數const通常用于參數為指針或引用的情況,若輸入參數采用“值傳遞”方式,由于函數將自動產生臨時變量用于復制該參數,該參數本就不需要保護,所以不用const修飾。

const修飾類對象/對象指針/對象引用

const修飾類對象表示該對象為常量對象,其中的任何成員都不能被修改。對于對象指針和對象引用也是一樣。

const修飾的對象,該對象的任何非const成員函數都不能被調用,因為任何非const成員函數會有修改成員變量的企圖。

例如:

class AAA

{
?? void func1();

void func2() const;

}

const AAA aObj;

aObj.func1();?錯誤

aObj.func2();?正確

?

const AAA* aObj = new AAA();

aObj->func1();?錯誤

aObj->func2();?正確

const修飾數據成員

const數據成員只在某個對象生存期內是常量,而對于整個類而言卻是可變的。因為類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類聲明中初始化const數據成員,因為類的對象未被創建時,編譯器不知道const?數據成員的值是什么,例如:

class A
{
??? const int size = 100; //錯誤
??? int array[size];????? ?//錯誤,未知的size
}

const數據成員的初始化只能在類的構造函數的初始化列表中進行。要想建立在整個類中都恒定的常量,可以用類中的枚舉常量來實現,例如:

class A
{


  enum {size1=100, size2 = 200 };
  int array1[size1];
  int array2[size2];


}

枚舉常量不會占用對象的存儲空間,他們在編譯時被全部求值。但是枚舉常量的隱含數據類型是整數,其最大值有限,且不能表示浮點數。

const修飾成員函數

const修飾類的成員函數,用const修飾的成員函數不能改變對象的成員變量。一般把const寫在成員函數的最后:

class A

{

?? …

void function()const; //常成員函數,?它不改變對象的成員變量.?也不能調用類中任何非const成員函數。

}

對于const類對象/指針/引用,只能調用類的const成員函數。

const修飾成員函數的返回值

1、一般情況下,函數的返回值為某個對象時,如果將其聲明為const時,多用于操作符的重載。通常,不建議用const修飾函數的返回值類型為某個對象或對某個對象引用的情況。原因如下:如果返回const對象,或返回const對象的引用,則返回值具有const屬性,返回實例只能訪問類A中的公有(保護)數據成員和const成員函數,并且不允許對其進行賦值操作,這在一般情況下很少用到。?
2
、如果給采用指針傳遞方式的函數返回值加const修飾,那么函數返回值(即指針所指的內容)不能被修改,該返回值只能被賦給加const?修飾的同類型指針:

const char * GetString(void);

如下語句將出現編譯錯誤:

char *str=GetString();

正確的用法是:

const char *str=GetString();

3、函數返回值采用引用傳遞的場合不多,這種方式一般只出現在類的賻值函數中,目的是為了實現鏈式表達。如:

class A
{
??? A &operate= (const A &other); //賦值函數
}
A a,b,c; //a,b,cA的對象

a=b=c; ??//正常
(a=B)=c; //不正常,但是合法

若賦值函數的返回值加const修飾,那么該返回值的內容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。

const常量與define宏定義的區別

(1)?編譯器處理方式不同

define宏是在預處理階段展開。

const常量是編譯運行階段使用。

(2)類型和安全檢查不同

define宏沒有類型,不做任何類型檢查,僅僅是展開。

const常量有具體的類型,在編譯階段會執行類型檢查。

(3)?存儲方式不同

define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。

const常量會在內存中分配(可以是堆中也可以是棧中)

volatile關鍵字

volatile的本意是“易變的”,volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

當要求使用volatile?聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,即使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被寄存。例如:

volatile int i=10;

int a = i;

。。。//其他代碼,并未明確告訴編譯器,對i進行過操作

int b = i;

volatile?指出?i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由于編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i里面讀。這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。

注意,在vc6中,一般調試模式沒有進行代碼優化,所以這個關鍵字的作用看不出來。下面通過插入匯編代碼,測試有無volatile關鍵字,對程序最終代碼的影響。首先用classwizard建一個win32 console工程,插入一個voltest.cpp文件,輸入下面的代碼:

#include <stdio.h>

void main()

{

int i=10;

int a = i;

?

printf("i= %d/n",a);

//下面匯編語句的作用就是改變內存中i的值,但是又不讓編譯器知道

__asm {

?mov dword ptr [ebp-4], 20h

}

?

int b = i;

printf("i= %d/n",b);

}

然后,在調試版本模式運行程序,輸出結果如下:

i = 10

i = 32

然后,在release版本模式運行程序,輸出結果如下:

i = 10

i = 10

輸出的結果明顯表明,release模式下,編譯器對代碼進行了優化,第二次沒有輸出正確的i值。下面,我們把?i的聲明加上volatile關鍵字,看看有什么變化:

#include <stdio.h>

void main()

{

volatile int i=10;

int a = i;

?

printf("i= %d/n",a);

__asm {

?mov dword ptr [ebp-4], 20h

}

?

int b = i;

printf("i= %d/n",b);

}

分別在調試版本和release版本運行程序,輸出都是:

i = 10

i = 32

這說明這個關鍵字發揮了它的作用!

?

關于volatile的補充信息:

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:

????1).?并行設備的硬件寄存器(如:狀態寄存器)

??? 2).?一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

??? 3).?多線程應用中被幾個任務共享的變量

????我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile的重要性:

??? 1).?一個參數既可以是const還可以是volatile嗎?解釋為什么。

??? 2).?一個指針可以是volatile?嗎?解釋為什么。

??? 3).?下面的函數有什么錯誤:

???????? int square(volatile int *ptr)

???????? {

????????????? return *ptr * *ptr;

???????? }

????下面是答案:

??? 1).?是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

??? 2).?是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。

??? 3).?這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:

??? int square(volatile int *ptr)

??? {

???????? int a,b;

???????? a = *ptr;

????????b = *ptr;

???????? return a * b;

???? }

????由于*ptr的值可能被意想不到地該變,因此ab可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

???? long square(volatile int *ptr)

???? {

??????????? int a;

??????????? a = *ptr;

??????????? return a * a;

???? }

mutable關鍵字

mutalbe的中文意思是“可變的,易變的”,跟constant(既C++中的const)是反義詞。在C++中,mutable也是為了突破const的限制而設置的。被mutable修飾的變量(mutable只能由于修飾類的非靜態數據成員),將永遠處于可變的狀態,即使在一個const函數中。

我們知道,假如類的成員函數不會改變對象的狀態,那么這個成員函數一般會聲明為const。但是,有些時候,我們需要在const的函數里面修改一些跟類狀態無關的數據成員,那么這個數據成員就應該被mutalbe來修飾。下面是一個小例子:

class ClxTest

{

 public:

  void Output() const;

};

?

void ClxTest::Output() const

{

 cout << "Output for test!" << endl;

}

?

void OutputTest(const ClxTest& lx)

{

 lx.Output();

}

ClxTest的成員函數Output是用來輸出的,不會修改類的狀態,所以被聲明為const

函數OutputTest也是用來輸出的,里面調用了對象lxOutput輸出方法,為了防止在函數中調用成員函數修改任何成員變量,所以參數也被const修飾。

假如現在,我們要增添一個功能:計算每個對象的輸出次數。假如用來計數的變量是普通的變量的話,那么在const成員函數Output里面是不能修改該變量的值的;而該變量跟對象的狀態無關,所以應該為了修改該變量而去掉Outputconst屬性。這個時候,就該我們的mutable出場了,只要用mutalbe來修飾這個變量,所有問題就迎刃而解了。下面是修改過的代碼:

class ClxTest

{

 public:

  ClxTest();

  ~ClxTest();

?

  void Output() const;

  int GetOutputTimes() const;?

 private:

  mutable?int m_iTimes;

};?

ClxTest::ClxTest()

{

 m_iTimes = 0;

}?

ClxTest::~ClxTest()

{}

void ClxTest::Output() const

{

 cout << "Output for test!" << endl;

 m_iTimes++;

}?

int ClxTest::GetOutputTimes() const

{

 return m_iTimes;

}?

void OutputTest(const ClxTest& lx)

{

 cout << lx.GetOutputTimes() << endl;

 lx.Output();

 cout << lx.GetOutputTimes() << endl;

}

計數器m_iTimesmutable修飾,那么它就可以突破const的限制,在被const修飾的函數里面也能被修改。

參考:http://blog.csdn.net/wuliming_sc/article/details/3717017?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383994.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據鏈路

廣播信道的數據鏈路層 局域網的優點 網絡為一個單位所擁有, 地理范圍和站點數有限 局域網具有廣播特性, 可以從一個站點方便地訪問到整個網絡. 各個主機之間可以共享資源, 無論是局域網上的硬件資源還是局域網上的軟件資源 便于系統的擴展換和演化, 各個設備之間的位置可靈…

UVa11809

【題目描述】 傳送門 【題目分析】 終于把這道題做完了&#xff0c;之前一直連題意都看不懂。實在不行上網找了一下大佬的博客&#xff0c;看懂題意后自己寫&#xff0c;發現讀入很難處理&#xff0c;就又學習了一下大佬的讀入方法&#xff0c;用的是C里面的sstream&#xf…

數據鏈路層:基本概念

數據鏈路層的定義 對數據鏈路層有對上的網絡層接口. 對下提供物理層的接口. 定義合適的傳輸差錯率 對傳輸流進行管理, 以免快速的傳輸的數據被淹沒. 比如發送端發送信號太快, 接受方接受速度較慢, 此時數據鏈路層就需要提供一定的功能解決這個問題 物理層上傳輸的基本單元是…

C++的沉迷與愛戀

每年的 09/28 於我都是一個特殊的日子 -- 不只是因為教師節。今年很特殊地沒有普天同慶&#xff0c;那麼我就寫篇文章自己慶祝一下好了。我於今年七月發表了一本著作《多型與虛擬》和一本譯作《深度探索C物件模型》&#xff0c;獲得很大的回響。這些作品都不是針對 C 的完全初學…

Insertion Sort——打表找規律

【題目描述】 Insertion sort is a simple sorting algorithm that builds the final sorted array one item at an iteration.More precisely, insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. At each iteration…

數據鏈路層: 可靠性傳輸 六個協議

可靠性傳輸 1. 差錯控制 發送方將數據幀發送, 但是當發送方發送的是一個 1的時候此時接受方卻接受的是一個 0. (1)校驗 接收方如果幀校驗接受到的幀沒有問題, 則對發送方發送一個肯定性的確認, 當對這個數據幀進行校驗發現這個幀有問題的時候, 此時接受方一種是將這個數據幀…

c語言實現配置文件的讀寫

配置文件的格式如下&#xff1a; key1 value1 key2 value2 . . . 名值對以一個鏈接&#xff0c;一條記錄以換行符分割 頭文件&#xff1a; #include<stdio.h> #include<stdlib.h> #include <string.h> 函數原型&#xff1a; void trim(char *strIn, char *…

Educational Codeforces Round 73 (Rated for Div. 2)

A 很簡單的一個模擬&#xff0c;只要前面的數字有兩個以上就能合成后面的&#xff0c;我們進行一遍合成看能不能出現2048就可以了。 #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<iostream> #include&…

數據鏈路層: HDLC

一. 協議機 發送方和接收方. 同時有限狀態機把協議形式化為一個四元組 (S,M,I,T), 其中你S表示進程和信道可能進入的集合, M 表示數據幀的狀態, I 表示進程的初始狀態, T 表示兩兩狀態之間的轉化. 每個系統狀態可以分為發送狀態, 接受狀態和信道狀態. 把狀態用一個點進行表示,…

Miller_Rabin算法

為了測試一個大整數是不是素數&#xff0c;我們不能夠使用傳統的測試是否有因子的方法&#xff0c;因為那樣的時間復雜度至少也是O(n)O(n)O(n)&#xff0c;空間復雜度是O(n)O(n)O(n)&#xff08;使用線性篩數法&#xff09;&#xff0c;時間復雜度還好說&#xff0c;空間復雜度…

bob-tong 字符串函數之Strtok()函數

https://www.cnblogs.com/Bob-tong/p/6610806.html Strtok()函數詳解&#xff1a; 該函數包含在"string.h"頭文件中 函數原型&#xff1a; char* strtok (char* str,constchar* delimiters ); 函數功能&#xff1a; ??切割字符串&#xff0c;將str切分成一個個子…

數據鏈路層:SLIP(串型線路IP) PPP(點對點協議)

SLIP 沒有差錯控制, 傳輸時必須知道對方IP, 傳輸使用于低速業務 19.2k.應用非常受限 PPP協議 1. PPP協議功能 處理錯誤檢測 支持多協議(IP, IPX, DECnet 等) 連接時允許協商 IP 地址 允許身份驗證 2. PPP 的組成 串型鏈路上封裝數據報, 即支持異步鏈路也支持面向 比特…

Honeycomb——BFS

【題目描述】 傳送門 【題目分析】 看起來很復雜好像還要建圖什么的&#xff0c;其實直接在原圖上BFS就可以了&#xff0c;設置一下方向數組&#xff0c;然后直接跑就可以了。 【AC代碼】 #include<cstdio> #include<cstring> #include<algorithm> #inc…

C語言中strspn()函數和strcspn()函數的對比使用

C語言strspn()函數&#xff1a;計算字符串str中連續有幾個字符都屬于字符串accept 頭文件&#xff1a;#include <string.h> strspn() 函數用來計算字符串 str 中連續有幾個字符都屬于字符串 accept&#xff0c;其原型為&#xff1a; size_t strspn(const char *str, con…

Codeforces Round #587 (Div. 3)

A 只要每兩個都不一樣就可以&#xff0c;一旦出現兩個一樣的就改一個。 #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cctype> #include<queue> #include<set>using namespace std;typede…

信道分配 以太網

1.頻分復用 將信道分為多個頻帶, 用戶得到某個頻帶后,在通信的過程中, 自始至終都都占用這個信道.即頻分復用中, 所有用戶同時占用不同頻帶的信道 2. 時分信道 將時間劃分為一段一段的等長時分復用幀, 每個用戶在不同時間占用相同的數據幀 3. CSMA/CD 載波監聽多點接入/碰撞…

strpbrk函數

http://blog.csdn.net/tommy_wxie/article/details/7554332 函數原型&#xff1a;extern char *strpbrk(char *str1, char *str2) 參數說明&#xff1a;str1待比較的字符串&#xff0c;str2為指定被搜索的字符串。 所在庫名&#xff1a;#include <string.h> …

網絡層網絡層服務及其 IP 地址

ARP 協議功能 將 IP 地址通過廣播(一個網段, 不能跨路由器), 目標 MAC 地址是FFFFFFFF 解析目標IP地址的 MAC 地址. IP 協議 網絡層的一個協議, 是一個協議的統稱, 包括 ARP(地址解析協議) 協議, ICMP(網絡控制報文協議) 協議, IGMP(網際組管理協議) 協議. 其中 ICMP 和 IG…

隨機生成1024個數,存入一段內存,用指針實現獲取1024個數的最大數地址,最小數地址

http://blog.csdn.net/itcastcpp//details/39277193 題目&#xff1a;隨機生成1024個數&#xff0c;存入一段內存&#xff0c;用指針實現獲取1024個數的最大數地址&#xff0c;最小數地址&#xff0c;具體實現如下&#xff1a; [cpp] view plaincopy #include<stdlib.h> …

UVa11134

【題目分析】 覺得是一道挺考驗貪心掌握程度的題目&#xff0c;我就算知道是要用貪心而且肯定和區間有關&#xff0c;肯定要進行一下排序什么的我還是沒有找到合適的貪心策略。 經過大佬的博客后我才明白如何進行貪心。 如果沒有任何提示看這道題&#xff0c;首先&#xff0…