C++11 關鍵字 const 使用小結

Const 的作用及歷史

const (computer programming) – Wikipedia

一、歷史

按理來說,要想瞭解一件事物提出的原因,最好的辦法就是去尋找當時的歷史背景,以及圍繞這件事所發生的故事。

可是非常抱歉,我並沒沒有找到C語言中const 提出的背景,但是一個可以參考的歷史是,常量這種數據形式早在匯編語言中就有所體現,匯編語言中的constant 是一個確定的數值,在匯編階段就可以確定直接編碼在於指令代碼中,不是保存在寄存器中的可以變化的量。

常量是需求,C 語言沒理由不保留這個傳統,自然而然的const 關鍵字出現瞭。

二、C和C++的異同點

顧名思義,const 最基礎的作用就是保證數據不會被修改,僅僅可讀而已。這就好比一份沒有write 權限的文件一樣,隻能遠觀而已。

const 是C語言32個關鍵字(C++中有49個)中的一個,主要起類型修飾的作用,可以理解為變量的屬性,比方說const int a = 10 從右往左看int a = 10 是定義並初始化瞭一個變量a 等於10,隨後使用const 修飾這個變量,告訴編譯器,這個變量是不可修改的。為瞭維護程序的安全性,由於const 一旦修飾就無法再更改瞭,那麼const int a; 會生成一個值隨機且永遠無法修改的量,這樣非常不安全,所以C 的編譯器會要求你必須在定義的時候就立馬初始化。從這裡也可以看出const關鍵字的強硬之處。

到底const該放在哪?

在詳細討論const的用法之前,必須首先明白,const 是C語言中的一個類型限定符(type quailier),是類型的一個部分,且const 越靠近誰,就修飾誰是常量類型。

從C語言的基礎數據類型來看,基本上可以抽象為一下幾個類別

  • 基礎數據類型(整型,浮點)

對於基礎數據類型,使用const就單純定義為一個不可修改的量,此時由於不涉及其他的類型限制符,所以const放在哪裡都是有效的

const int i = 10 \(\Leftrightarrow\) int const i = 10 但是一般const在前。

  • 指針類型(指針)

相比於基礎數據類型,指針類型存在很大的不同。

不使用const修飾的指針,此時表示該指針一定指向一個變量,當指向const修飾的變量是就會報錯

const int a = 10;
int * ptr = a; // error

const 放在 int *前時,表示指針類型是 const int 類型,那麼依據指針類型的定義,該指針必定指向一個const int 類型的量,即常量。

const int b = 100;
const int * a = &b;

const 放在 int * 後面時,int * const a ,顯然根據常規的指針類型的定義,我們隻能推測出這是一個指向int類型的指針,那麼const起什麼作用呢?(見如下代碼)

int c = 10, b = 20;
const int b = 30;
int * const a = &c; // 此處沒有報錯,證明*號前面是指針類型,這條真理沒錯
//1.  可當我們嘗試修改指向的時候
a = &b; 
// 此處會報錯!這表明const靠近變量名的時候表示指針指向一個變量後就無法更改瞭

//2. 如果一開始就不初始化int * const a呢?
int * const a; // 此處會報錯

//3. 如果嘗試讓他指向一個const量呢?
int * const a = &b; // 此處會報錯

以此類推可以得到一個指向const變量的無法修改指向的指針

const int b = 10;
const int * const a = &b;

所以可以給出總結

  • const 靠近變量名的時候表示指針必須指向一個類型與指針類型相同的變量
  • 一旦指向就無法更改指向
  • 無法指向常量

復雜數據類型(枚舉,結構,共用)

針對復雜類型,由於出現瞭簡單類型的嵌套,自然會有const 的嵌套關系,下面以結構體來舉例子

const嵌套在結構體內部時。

typedef struct a {
	const int b;
	int c;
}A;

int main() {
	A aa;
	aa.b = 10; // 此處會報錯
}

在C語言中,在結構體內部使用const修飾不會報錯的,但是此變量再也無法修改,意味著這是一個無效量,既無法初始化,也無法修改(但是得益於C++的面向對象機制,即使如此我們還是可以定義const並且給他賦值)。

當定義結構體的時候使用const

const A bb;

此時也會報錯,而且相對來說比上面還嚴重,此時結構體內部的所有值都是亂的,且無法修改。

而C++中由於引入瞭幾種新的編程模式,const 的作用范圍又進一步被擴充。

類中屬性與成員函數

結構體的遺留問題(即類的常量屬性)

這裡先來解決前面C語言中的結構體問題,需求是想在結構體內部定義const變量,知道結構體內部的變量是無法直接初始化的,而C++中結構體可以理解為類,隻不過權限不同而已,同樣可以擁有構造函數。

那麼是不是可以在構造函數中初始化呢?(下面代碼會報錯)

struct a {
	a() {
		b = 100;
	}
	const int b;
};

不是我們想的那樣,不過也非常接近,對於初始化類的變量還有一種方法,使用初始化列表(類的初始化列表的優先級是非常高的)

struct a {
	a() : b(100) {;}

  const int b;
};

或者還有

struct a {
    const int b = 100;
};

利用C++特性直接賦值,而此段代碼在C語言中會報錯,這也是C與C++不同的一個地方。

如此就完美解決瞭結構體const量 問題。

類的靜態變量vs const 變量

static 也是一個修飾符,確定的是變量的生存期。const覺得變量的可讀性,有這樣一條語句在類中和main函數中存在不同的意義

static const int a; 
// 此語句在main中會報錯,由於未初始化
// 在類中不會

這是由於static不會影響const的表達,在main函數中說明此變量就是const類型,確實需要立馬賦值。而在類中可以不那麼著急,可以把類中的static變量理解為一個申明,在類的外面或者裡面直接定義都可以,不會報錯。

函數const 以及類成員函數的const修飾

普通函數的const

函數const 首先想到的是const 變量返回值。但是這其實是沒太大意義的

const修飾返回值其實完全沒有發揮作用,屬於無效修飾。同樣的使用const修飾形式參數的時候也是如此,並不會限定你傳入的是const還是普通變量,本質在於這一過程發生原因是由於值傳遞,不論是返回const 還是使用const 修飾形式參數,內部都發生瞭變量的創建與賦值

const修飾形參的例子,

int fun(const int a) {
	// a = 10 會報錯
	return a
}

int main() {
	int c = 10;
	int d = fun(c);  // 不會報錯
}

如上,c傳入的時候是把c的值拿到,然後函數壓棧,創建一個const int 變量a 且立馬初始化為c的值,如此就在函數內部生成瞭一個const 變量。跟傳入什麼值完全沒有關系。

成員函數的const尾修飾

這屬於C++的特性,成員函數尾巴加上一個const 限制此函數對對象的修改,且提高瞭代碼的可讀性。

class A {
private:
	int a;

public:
	static int B;
	int getA() const {
		A::B = 100; // 此處不會報錯
		a = 100; // 這裡會報錯
		return a;
	}
};

int A::B = 100;

使用const修飾成員函數會使該函數變成const member function 此類型無法修改對象的數據,但是可以修改可修改的靜態變量。

  • 引用

引用相對來說沒有指針那麼多的變種,引用的const 修飾也僅僅局限於讓引用變量無法修改指向這一點上。

  • 在補充一點

const修飾類靜態整型變量的時候可以在類內部直接初始化(浮點數仍然是不行的)。

到此這篇關於C++11 關鍵字 const 使用小結的文章就介紹到這瞭,更多相關C++11 關鍵字 const內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: