天天看點

【Effective C++】const的使用

對于const,最為熟悉的就是,它允許你指定一個語義限制,也就是指定一個不該被改動的對象,而編譯器會強制實施這項限制。const 修飾的值是隻讀的變量,而不是常量,其值在編譯時不能被使用,因為編譯器在編譯時不知道其存儲的内容。下面程式說明

// .c file , c project
#include <stdio.h>

const int Size = 100;

int main()
{
	int arr[Size];  //編譯出錯

	return 0;
}
           

C語言中,編譯出錯,表明 const 修飾的值不是常量(C語言)。但在C++中同樣的代碼卻不會出現編譯錯誤,因為C++擴充了const 的含義,是以C++和C在某些細節上還是有差別的,不過const在C++中更多的是應用在類中,這裡主要讨論C++中的const 。

含指針情況下,const 到底修飾誰?先忽略類型名,const 離誰近,就修飾誰。或者,如果關鍵字 const 出現在 * 左邊,表示被指的對象不可改;如果出現在 * 右邊,則表示指針自身不可改,如果兩邊都有,那就是指針和指針所指的對象不可改。

下面就重點探讨C++類中的 const。

一、const 資料成員

const 資料成員隻在某個對象生存期内是隻讀變量,而對于整個類而言是可變的,因為類可以建立多個對象,不同的對象其 const 資料成員可以是不同值,const 資料成員必須被初始化,且必須使用初始化清單,不能在構造函數的函數體中初始化。

/*錯誤初始化方式*/
class Test
{
public:
	Test()
	{
		i = 5;   //錯誤
	}

private:
	const int i;
};

/*正确初始化方式*/
class Test
{
public:
	Test():i(5) {}

private:
	const int i;
};
           

因為定義是在類本身定義的時候進行的(class {}範圍),調用構造函數的時候,首先是給該執行個體(類的具體對象)配置設定空間,使用初始化清單就是在配置設定空間的時候,同時将其空間初始化,而在構造函數的函數體内,所有變量(常量)的空間都已經配置設定好了,對于const成員變量本身是不能改變其值的,構造函數的函數體{}内的操作隻能是指派,對const 成員變量指派操作,自然是非法操作。是以const 資料成員的初始化隻能在類構造函數的初始化表中進行。

二、const 函數

重頭戲來了,const 最具威力的用法是面對函數聲明時的應用,在一個函數聲明式内,const 可以和函數傳回值、各參數、函數本身(成員函數)産生關聯。

1、const 函數傳回值,即令函數傳回一個const 類型,最突出的應用就是運算符重載時保證與内置類型運算符相容,這樣在某些運算符重載時,就不會自創出一些沒意義的自定義類型運算符。

class Rational { ... };
const Rational operator* (const Rational& lhs, const Rational& rhs);
           

這樣當指向下列操作時就會編譯出錯。

Rational a, b, c;
(a * b) = c;    //error
           

如果函數傳回值不為const 類型,那麼上面的操作是不會出錯的,但是這與内置類型不相容,内置類型不允許對兩個數值的乘積再做一次指派。隻要const 了函數傳回至,那麼這個函數調用就别想作左值。

2、const 參數,保證函數内部不會修改該值,const 作為形參自然伴随着引用或指針,不然就沒有實際意義,常用的是 const 引用形參,這樣既確定了被調函數不會改變主調函數的資料,又不會像非指針、非引用的參數那樣要發生複制,提高效率,雖然傳值形式的函數調用也不會修改主調函數的資料,但函數調用時需要額外開辟棧空間拷貝一份副本。需要注意的是形參為const 時,最好實參也為const。如果主函數裡面定義了一個const 變量,那麼操作該變量的函數的對應形參就必須是const。

3、const 成員函數,将const 實施于成員函數的目的,是為了确認該成員函數可作用于const 對象身上。成員函數聲明為const 類型,就意味着該函數不允許修改類中的成員資料(除非成員資料标記為 mutable),這樣編譯器看到函數定義為const ,就知道并認為該函數不會去修改類成員資料。

const 成員函數的定義(聲明)方式怪怪的,const 放在函數名後面,函數體前面

class Test
{
public:
	int fun(int random_arg) const
	{
		//code
	}
};
           

實際上上面的函數可了解為以下形式

int fun(const Test* this, int random_arg) const {	/*code*/  }
           

修飾符const 的作用是限制指針this 指向的對象(const 在 * 左邊)隻讀,也就是調用該函數的對象是隻讀,該函數不可修改該對象,一旦發現函數體内有修改該對象資料的行為就報錯。C++中,this指針是隐藏的,是以就有了前面的const 成員函數聲明方式。

C++規定,const 對象隻能與const 成員函數連用,目的是為了保護const 對象資料不被改寫。另外const成員函數的聲明與定義形式需保持一緻性,否則編譯器将不認為二者是同一個函數。

總結:

1)const成員函數可以通路非const對象的非const資料成員、const資料成員,也可以通路const對象内的所有資料成員;

2)非const成員函數可以通路非const對象的非const資料成員、const資料成員,但不可以通路const對象的任意資料成員;

3)作為一種良好的程式設計風格,在聲明一個成員函數時,若該成員函數并不對資料成員進行修改操作,應心可能将該成員函數聲明為const 成員函數。

繼續閱讀