程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C/C++語言中關於const用法的總結

C/C++語言中關於const用法的總結

編輯:C++入門知識

C/C++語言中關於const用法的總結


一. const的基本功能與用法

1.將限定符聲明為只讀

使用方法如下,在類型前/後加上關鍵字const,該變量必須被初始化,否則編譯錯誤;該變量不能被重新賦值,否則也編譯錯誤。
舉例:

const int i = 50;   // 編譯正確
const int j;        // 編譯錯誤
int k = 0;
i = k;              // 編譯錯誤
k = i;              // 編譯正確

2.用於修飾函數形參,保護參數使其不被修改

用法1:若形參為const A* a,則不能改變函數所傳遞的指針內容,這樣對指針所指向的內容起到保護作用,這裡需要注意的是,該修飾不能改變指針所指向地址所存儲的內容,但是指針a所指向的地址可以被改變,具體例子如下:

void Test(const int *a)
{
    *a = 1;          //錯誤,*a不能被賦值
    a = new int(10086);  //正確,為指針a開辟新的空間,並令*a=2
}

int main()
{
    int *a = new int(10000);
    Test(a);
    return 0;
}

用法2:若形參為const A& a,則改變函數傳遞進來的引用對象,從而保護了原對象的屬性。

對於自定義的數據類型,用引用傳遞速度較快,如果不想改變原值,就可以用const來保護參數,如以下例子:

void Test(const int &a) //保護L7中的a不會被改變
{
    a = 2;//錯誤,a不能給常量賦值
}

int main()
{
    int a = 3;
    Test(a);
    return 0;
}

事實上對於內置型數據類型(如以上例子中的int類型),用引用傳遞不會使速度更快。如果是用引用傳參,一般是為了改變參數值;如果不想改變參數的值,直接值傳遞即可,不必使用const修飾。而對於自定義數據類型的輸入參數,為了提高速度和效率,應使用“const + 引用傳遞”代替值傳遞。例如:

將函數 void Test(A a) 改為-> void Test(const A &a)

3.用於修飾函數返回值

用法1:用const修飾返回值為對象本身(非引用和指針)的情況多用於二目操作符重載函數並產生新對象的時候
舉例:

const Rational operator*(const Rational& lhs, const Rational& rhs) 
{ 
    return Rational(lhs.numerator() * rhs.numerator(), 
lhs.denominator() * rhs.denominator()); 
} 
Rational a,b; 
Radional c; 
(a*b) = c;//錯誤

用法2:不建議用const修飾函數的返回值類型為某個對象或對某個對象引用的情況。原因如下:如果返回值為某個對象為const(const A test = A 實例)或某個對象的引用為const(const A& test = A實例) ,則返回值具有const屬性,則返回實例只能訪問類A中的公有(保護)數據成員和const成員函數,並且不允許對其進行賦值操作,這在一般情況下很少用到,具體例子如下:

class A
{
public:
    int y;
    A(int y):x(x),y(y){};
    void Sety(int y){this->y = y;}
};

const A Test1(A a)
{
    return a;
}

const A& Test2(A &a)
{
    return a;
}

int main()
{
    A a(2);
    Test1(a).Sety(3);//錯誤,因為Test1(a)的返回值是個const,不能被Sety(3)修改
    Test2(a).Sety(3);//錯誤,因為Test1(a)的返回值是個const,不能被Sety(3)修改
    return 0;
}

用法3:如果給采用“指針傳遞”方式的函數返回值加const修飾,那麼函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。例子如下:

const char * GetString(void){}
int main()
{
    char *str1=GetString();//錯誤
    const char *str2=GetString();//正確
    return 0;
}

用法4:函數返回值采用“引用傳遞”的場合不多,這種方式一般只出現在類的賦值函數中,目的是為了實現鏈式表達。例子如下:

class A
{
    // 以下賦值函數的返回值加const修飾,該返回值的內容不允許修改
    A &operate = (const A &other); 
}
A a, b, c;  // a,b,c為A的對象
a = b = c;      // 正確
(a = b) = c;    // 錯誤,a = b的返回值不允許被再賦值

4.在類成員函數的函數體後加關鍵字const

在類成員函數的函數體後加關鍵字const,形如:void fun() const; 在函數過程中不會修改數據成員。如果在編寫const成員函數時,不慎修改了數據成員,或者調用了其他非const成員函數,編譯器將報錯,這大大提高了程序的健壯性。

如果不是在類的成員函數,沒有任何效果,void fun() const;void func();是一樣的。

5.在另一連接文件文件中引用常量

方法:在類型前添加關鍵字extern const,表示引用的是常量,因此該常量不允許被再次賦值,舉例如下:

extern const int i;       // 正確
extern const int j = 10;  // 錯誤,常量不可以被再次賦值

二.const常量與#define的區別

1.const常量有數據類型,而宏常量沒有數據類型

宏常量只進行簡單的字符替換,沒有類型安全檢查,並且在字符替換時可能會產生意料不到的錯誤,如:

#define I = 10
const long &i = 10;
// 由於編譯器的優化,使得在const long i=10時i不被分配內存
// 而是已10直接代入以後的引用中,以致在以後的代碼中沒有錯誤
//一旦你關閉所有優化措施,即使const long i = 10也會引起後面的編譯錯誤。
char h = I; // 正確
char h = i; // 編譯警告,可能由於數的截短帶來錯誤賦值

2.使用const可以避免不必要的內存分配

從匯編的角度來看,const定義常量只是給出了對應的內存地址, 而不是象#define一樣給出的是立即數,所以,const定義的常量在程序運行過程中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝。例子如下:

#define k Hello world!
const char pk[]=Hello world!;
printf(k);      // 為k分配了第一次內存
printf(pk);     // 為pk一次分配了內存,以後不再分配
printf(k);      // 為k分配了第二次內存
printf(pk);

三. 使用const的一些注意事項

1.修改const 所修飾的常量值

以下例子中,iconst修飾的變量,可以通過對i進行類型強制轉換,將地址賦給一個新的變量,對該新的變量再作修改即可以改變原來const 修飾的常值。

const int i=0;
int *p=(int*)&i;
p=100;

2.構造函數不能被聲明為const

3.const數據成員的初始化只能在類的構造函數的初始化表中進行

class A
{
public:
    const int a;
    A(int x):a(x)//正確
    {
        a = x;//錯誤
    }
};

4.在參數中使用const應該使用引用或指針,而不是一般的對象實例

合理利用const在成員函數中的三種用法(參數、返回值、函數),一般來說,不要輕易的將函數的返回值類型定為const;另外,除了重載操作符外一般不要將返回值類型定為對某個對象的const引用。

5.對於使用const修飾來指針的情況

對於以下情況,const放在變量聲明符的前後位置效果是一樣的,這種情況下不允許對指針a 的內容進行更改操作:

int i;
const int *a = &i;
int const*a = &i;

但是,如果const位於星號的左側,則const就是用來修飾指針所指向的變量,即該指針指向一個地址,該地址的內容不可變;如果const位於星號的右側,const就是修飾指針本身,即指針本身是常量

int i;
// 以下一行表示a是一個指針,可以任意指向int常量或者int變量
// 它總是把它所指向的目標當作一個int常量
// 也可以寫成int const* a
const int *a = &i;
// 以下一行表示a是一個指針常量,
// 初始化的時候必須固定指向一個int變量
// 之後就不能再指向別的地方了
int *const a = &i;

6.指針本身是常量,而指針所指向的內容不是常量,這種情況下不能對指針本身進行更改操作,如以下例子中a++是錯誤的:

int *const a = &i;
a++;  // 錯誤,a指針本身是常量,不能再指向別的地方

7.當指針本身和指針所指向的內容均為常量時

這種情況下可寫為:

const int * const a = &i;

8.const成員函數返回的引用,也是const

#include
using namespace std;
class A
{
public:
    int x;
    void set(int x){this->x = x;}
    // const成員函數返回的引用也是const,a
    // 如果把A&前面的const去掉會出錯
    // 因為返回的是一個const的對象,返回類型卻不是const
    // 返回的內容和返回的類型不符
    const A& Test1()const
    {
    // 錯誤。這是const成員函數的特點
    x = 2;
    // 不限於*this。不管返回的是什麼,哪怕是一個定義為非const的對象,結果也是一樣的
    return *this;
    }
};

int main()
{
    A a, b;
    // 正確,雖然返回的是一個const,卻用另一個非const來接收
    b = a.Test1();
    // 錯誤,既然是別名,那麼別名的類型要與原來的類型相同
    A &c = a.Test1();
    // 正確雖然在a.Test1()中a不能改變,但是這裡已經出了這個成員函數的作用域
    a.set(2);
    // 正確,b接收了a.Test1()返回的數據的內容,但是它不是const
    b.set(2);
    // 錯誤。a.Test1()是一個對象,這個對象是它的返回值
    // 雖然沒有名字,但是它就是a.Test1()的返回值
    // 值是a.Test1()返回的值,類型是a.Test1()返回的類型
    a.Test1().set(2);
    return 0;
}

9.mutable將數據聲明為可變數據成員

在C++語言中,mutable是使用較少的關鍵字,它的作用是:如果一個函數被const 修飾,那麼它將無法修改其成員變量的,但是如果一個成員變量是被mutable修飾的話,則可以修改。

mutable 可以用來指出,即使成員函數或者類變量為const,其某個成員也可以被修改。反過來說,可變數據成員永遠不能成為const,即使它是const對象的成員。

class A
{
public:
    int x;
    mutable int y;
    A(int a, int b):x(a),y(b){}
};

int main()
{
    const A a(0, 0); // const對象必須初始化
    a.x = 1;         // 錯誤
    a.y = 2;         // 正確,mutable修飾使得成員可被修改,即使對象a為const
    return 0;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved