const修飾普通變量和指針
const修飾變量,一般有兩種寫法:
const TYPE value;
TYPE const value;
這兩種寫法在本質上是一樣的。它的含義是:const修飾的類型為TYPE的變量value是不可變的。對于一個非指針的類型TYPE,無論怎么寫,都是一個含義,即value值不可變。?例如:
const int nValue;??? //nValue是const
int const nValue;??? //nValue是const
但是對于指針類型的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();?錯誤
const修飾數據成員
const數據成員只在某個對象生存期內是常量,而對于整個類而言卻是可變的。因為類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類聲明中初始化const數據成員,因為類的對象未被創建時,編譯器不知道const?數據成員的值是什么,例如:
{
??? const int size = 100; //錯誤
??? int array[size];????? ?//錯誤,未知的size
}
const數據成員的初始化只能在類的構造函數的初始化列表中進行。要想建立在整個類中都恒定的常量,可以用類中的枚舉常量來實現,例如:
{
…
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?修飾的同類型指針:
如下語句將出現編譯錯誤:
正確的用法是:
3、函數返回值采用“引用傳遞”的場合不多,這種方式一般只出現在類的賻值函數中,目的是為了實現鏈式表達。如:
{…
??? A &operate= (const A &other); //賦值函數
}
A a,b,c; //a,b,c為A的對象
…
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的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
???? 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也是用來輸出的,里面調用了對象lx的Output輸出方法,為了防止在函數中調用成員函數修改任何成員變量,所以參數也被const修飾。
假如現在,我們要增添一個功能:計算每個對象的輸出次數。假如用來計數的變量是普通的變量的話,那么在const成員函數Output里面是不能修改該變量的值的;而該變量跟對象的狀態無關,所以應該為了修改該變量而去掉Output的const屬性。這個時候,就該我們的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;
}
參考:http://blog.csdn.net/wuliming_sc/article/details/3717017?