打算將基礎知識在看書的同時系統的整理一下,方便大家也方便自己。整理的知識盡量參照書本知識,比網上獲取的資料有更高的可信度。
程序中的特殊標識符或表達式,由於同時滿足:
(1)不可尋址(放在代碼區)
(2)值不可變
所以可視為文字常量。他們是 靜態數組名、枚舉變量、全局(靜態變量)首地址、#define定義的常量。
整型文字常量:
(1)前加0表示 八進制
(2)前加0x表示 十六進制
(3)後加L(推薦)或l,表示long類型
(4)後加U(或u)表示無符號數
eg.1024UL
同其他變量一樣被分配空間,可尋址。
const是在高級語言的語義層面上定義的,是編譯器在編譯期做語法檢測來保證,但是運行時,const變量不是在只讀內存中,而是和一般變量一樣放在數據區,所以一樣可以對其進行修改。
所以:常變量是一種加了特殊限制的變量,理解成“只讀”變量
即使是const修飾,也是可以修改的
#include
using namespace std;
void ShowValue(const int &i) {
cout<
3、常變量替換
如果常變量有初始化賦初值,那編譯器將該常變量在其他地方替換成文字常量
但是如果開始不初始化就會錯誤
如:
void DefineArray(const int n){
int B[n]={}; //error,數組大小在編譯期確定
}
int main(){
const int m=5;
int A[m]={}; //ok
}
4、文字常量和常變量尋址
int &r=5; //error,無法尋址文字常量,無法建立引用
const int &r=5; //ok,在數據區開辟一個值為5的無名整數量,然後將引用r與這個整形兩綁定
二、const用法
1、const的位置
int const *p; //指向常量的指針(即常指針,const修飾的是int),指向的對象是const型,不可以修改,但是指針p的指向可以修改
int *const p; //指針常量(const修飾的是int*),指針變量p是const型,它的指向不可修改,但是指向的對象可以修改
const和數據類型結合在一起 —>“常類型”。(看成一個整體)
修飾類型時,既可以放在放前面,也可以放在後面;用常類型聲明 or 定義變量,const只出現在變量前。
const和被修飾類型間不能有其他標識符存在。
引用本身可以理解成一個指針常量
故在引用前使用const沒有意義
int & const r4=i; //const是多余的,編譯器warning後忽略const存在
const配合二重指針,此例子中const在不同位置,結果不同
#include
using namespace std;
int main()
{
int const **p1; //不是指針常量,指向 int count*(“int const*”是一個 指向整型常量的指針)
int *const *p2; //不是指針常量,但所指的變量是指針常量(int *const,即指向整型的指針常量,指向不能修改)
int i=5;
int j=6;
const int *ptr1=&i;
int *const ptr2=&j;
p1=&ptr1;
p2=&ptr2;
cout<<**p1<
輸出:
5
6
上述p1和p2 賦值有講究,如果 p1=&ptr2 或 p2=ptr1 就會編譯錯誤
2、const修飾某個類 —> 常對象 和 常函數
const修飾對象–>常對象
const修飾成員函數—>常函數
在常函數中,不允許對任何成員變量進行修改
通過常對象,只能調用該對象的常函數
#include
using namespace std;
class A
{
int num;
public:
A() {num=5;}
void disp();
void disp() const;
void set(int n) {num=n;}
};
void A::disp() const {
cout<以上注意:
(1)如果常函數聲明和定義分開,都需要加const,否則編譯錯誤
只有類的非靜態成員函數可以被聲明為常函數
(2)如果一個類的兩個成員函數,返回值、函數名、參數列表完全相同,其中之一是const,則重載。因為 常成員函數的參數傳入this指針是const Class*類型的,參數不同,導致函數簽名不同。
非只讀對象(如a1)調用某個函數(如 disp()),先找非const版本,如果沒有,再調用const版本。而常對象,只能調用類中定義的常函數,否則編譯器報錯。
如果一個非const對象(如a1)調用函數,同時有const和非const版本的函數,我們希望其調用const函數。就必須建立該對象的常引用,或指向該對象的常指針來達到目的。如: (const A&)a1.disp(); 或 (const A *)&a1->disp();
(3)常對象創建後,其數據成員不允許在修改。所以顯示構造函數來初始化該對象非常重要。
常對象,全體成員數據成員都是常量看待。
類對象的非靜態常量成員必須在構造函數中初始化,且只能借助初始化列表進行。
3、const修飾函數參數+函數返回值
#include
using namespace std;
void disp1(const int &ri){
cout<
注意:
(1)const修飾參數,主要作用是被引用對象或被指向對象,如果只是形參,就沒有多少意義。如:void disp2(const int i),這裡的i在函數中改不改變,加不加const沒什麼影響。
不但如此,同時定義一個相似的用const修飾參數和不用const修飾參數的函數,會引起重定義錯誤。比如:任何整型表達式的值,都可以傳給int型參變量,也可以傳給const int型參變量,故不重載。
(2)當返回值是一個普通數據,而非引用,const修飾也沒多少意義。因為函數返回值是一個非左值,本來就不能改變其值。故其上 const int disp3(const int& ri),對返回值修飾然並卵。
(3)如果返回值為引用,用const修飾可以阻止對被引用對象修改,disp5(n)=6;是錯誤的
(4)常見的對const的誤解。
誤解一:用const修改的變量值一定是不能改變的。const修飾的變量可通過指針可間接修改。
如:
const int j=5;
void *p=(void *)&j;
int *ptr=(int *)p;
(*ptr)++;
誤解二:常引用或常指針,只能指向常變量,這是一個極大的誤解。常引用或者常指針只能說明不能通過該引用(或者該指針)去修改被引用的對象,至於被引用對象原來是什麼性質是無法由常引用(常指針)決定的。
三、const_cast 的用法
1、作用
const_cast 是 C++ 運算符,作用是去除符合類型中的const或者volatile
當大量使用const_cast是不明智的,只能說程序存在設計缺陷。使用方法見下例:
void constTest(){
int i;
cout<<"please input a integer:";
cin>>i;
const int a=i;
int& r=const_cast(a);//若寫成int& r=a;則發生編譯錯誤
++r;
cout<
輸入:
5
輸出:
6
總結:
(1)const_cast運算符的語法形式是const_cast< type> (expression)。 括號不可省略
(2)const_cast只能去除目標的const或者volatile屬性,不能進行不同類型的轉換。只能將 const type* 轉換為 type*,或者 const type & 轉換為 type &。
如下轉換就是錯誤的:
cons tint A={1,2,3};
char* p=const_cast< char*>(A); //不能由const int[]轉換為char*
(3)一個變量被定義為只讀變量(常變量),那麼它永遠是常變量。cosnt_cast取消的是間接引用時的改寫限制,而不能改變變量本身的const屬性。 如下就是錯誤的:
int j = const_cast< int> (i);
(4)利用傳統的C語言中的強制類型轉換也可以將 const type* 類型轉換為 type* 類型,或者將 const type& 轉換為 type& 類型。但是使用 const_cast 會更好一些,因為 const_cast 寫法復雜(提醒程序猿不要輕易轉換),轉換能力較弱,目的明確,不易出錯,易查bug;而C風格的強制類型轉換能力太強,風險較大。
參考資料
[1]陳剛.C++高級進階教程[M].武漢:武漢大學出版社,2008.