C++總結:C++中的const和constexpr(轉)

C++總結:C++中的const和constexpr

C++中的const可用於修飾變量、函數,且在不一樣的地方有着不一樣的含義,現總結以下。html

const的語義

C++中的const的目的是經過編譯器來保證對象的常量性,強制編譯器將全部可能違背const對象的常量性的操做都視爲error。數組

對象的常量性能夠分爲兩種:物理常量性(即每一個bit都不可改變)和邏輯常量性(即對象的表現保持不變)。C++中採用的是物理常量性,例以下面的例子:緩存

1安全

2函數

3優化

4this

5spa

6指針

7code

struct A {

    int *ptr;

};

int k = 5, r = 6;

const A a = {&k};

a.ptr = &r;

*a.ptr = 7;

a是const對象,則對a的任何成員進行賦值都會被視爲error,但若是不改動ptr,而是改動ptr指向的對象,編譯器就不會報錯。這實際上違背了邏輯常量性,由於A的表現已經改變了!

邏輯常量性的另外一個特色是,const對象中能夠有某些用戶不可見的域,改變它們不會違背邏輯常量性。Effective C++中的例子是:

1

2

3

4

5

6

7

8

9

class CTextBlock {

public:

    ...

    std::size_t length() const;

private:

    char *pText;

    std::size_t textLength;           

    bool lengthIsValid;               

};

CTextBlock對象每次調用length方法後,都會將當前的長度緩存到textLength成員中,而lengthIsValid對象則表示緩存的有效性。這個場景中textLength和lengthIsValid若是改變了,實際上是不違背CTextBlock對象的邏輯常量性的,但由於改變了對象中的某些bit,就會被編譯器阻止。C++中爲了解決此問題,增長了mutable關鍵字。

本部分總結:C++中const的語義是保證物理常量性,但經過mutable關鍵字能夠支持一部分的邏輯常量性。

const修飾變量

如上節所述,用const修飾變量的語義是要求編譯器去阻止全部對該變量的賦值行爲。所以,必須在const變量初始化時就提供給它初值:

1

2

3

const int i;

i = 5;

const int j = 10;

這個初值能夠是編譯時即肯定的值,也能夠是運行期才肯定的值。若是給整數類型的const變量一個編譯時初值,那麼能夠用這個變量做爲聲明數組時的長度:

1

2

3

4

const int COMPILE_CONST = 10;

const int RunTimeConst = cin.get();

int a1[COMPLIE_CONST];

int a2[RunTimeConst];

由於C++編譯器能夠將數組長度中出現的編譯時常量直接替換爲其字面值,至關於自動的宏替換。(gcc驗證發現,只有數組長度那裏直接作了替換,而其它用COMPILE_CONST賦值的地方並無進行替換。)

文件域的const變量默認是文件內可見的,若是須要在b.cpp中使用a.cpp中的const變量M,須要在M的初始化處增長extern:

1

2

3

4

5

extern const int M = 20;

extern const int M;

通常認爲將變量的定義放在.h文件中會致使全部include該.h文件的.cpp文件都有此變量的定義,在連接時會形成衝突。但將const變量的定義放在.h文件中是能夠的,編譯器會將這個變量放入每一個.cpp文件的匿名namespace中,於是屬因而不一樣變量,不會形成連接衝突。(注意:但若是頭文件中的const量的初始值依賴於某個函數,而每次調用此函數的返回值不固定的話,會致使不一樣的編譯單元中看到的該const量的值不相等。猜想:此時將該const量做爲某個類的static成員可能會解決此問題。)

const修飾指針與引用

const修飾引用時,其意義與修飾變量相同。但const在修飾指針時,規則就有些複雜了。

簡單的說,能夠將指針變量的類型按變量名左邊最近的‘*’分紅兩部分,右邊的部分表示指針變量本身的性質,而左邊的部分則表示它指向元素的性質:

const指針的解讀規則差很少就是這些了……

指針自身爲const表示不可對該指針進行賦值,而指向物爲const則表示不可對其指向進行賦值。所以能夠將引用當作是一個自身爲const的指針,而const引用則是const Type * const指針。

指向爲const的指針是不能夠賦值給指向爲非const的指針,const引用也不能夠賦值給非const引用,但反過來就沒有問題了,這也是爲了保證const語義不被破壞。

能夠用const_cast來去掉某個指針或引用的const性質,或者用static_cast來爲某個非const指針或引用加上const性質:

1

2

3

4

int i;

const int *cp = &i;

int *p = const_cast<int *>(cp);

const int *cp2 = static_cast<const int *>(p);

C++類中的this指針就是一個自身爲const的指針,而類的const方法中的this指針則是自身和指向都爲const的指針。

類中的const成員變量

 

類中的const成員變量可分爲兩種:非static常量和static常量。

非static常量:

類中的非static常量必須在構造函數的初始化列表中進行初始化,由於類中的非static成員是在進入構造函數的函數體以前就要構造完成的,而const常量在構造時就必須初始化,構造後的賦值會被編譯器阻止。

1

2

3

4

5

6

7

8

class B {

public:

    B(): name("aaa") {

        name = "bbb";

    }

private:

    const std::string name;

};

static常量:

static常量是在類中直接聲明的,但要在類外進行惟一的定義和初始值,經常使用的方法是在對應的.cpp中包含類的static常量的定義:

1

2

3

4

5

6

7

8

class A {

    ...

    static const std::string name;

};

const std::string A::name("aaa");

一個特例是,若是static常量的類型是內置的整數類型,如char、int、size_t等,那麼能夠在類中直接給出初始值,且不須要在類外再進行定義了。編譯器會將這種static常量直接替換爲相應的初始值,至關於宏替換。但若是在代碼中咱們像正常變量那樣使用這個static常量,如取它的地址,而不是像宏同樣只使用它的值,那麼咱們仍是須要在類外給它提供一個定義,但不須要初始值了(由於在聲明處已經有了)。

1

2

3

4

5

6

7

8

class A {

    ...

    static const int SIZE = 50;

};

const int A::SIZE = 50;

const修飾函數

C++中能夠用const去修飾一個類的非static成員函數,其語義是保證該函數所對應的對象自己的const性。在const成員函數中,全部可能違背this指針const性(const成員函數中的this指針是一個雙const指針)的操做都會被阻止,如對其它成員變量的賦值以及調用它們的非const方法、調用對象自己的非const方法。但對一個聲明爲mutable的成員變量所作的任何操做都不會被阻止。這裏保證了必定的邏輯常量性。

另外,const修飾函數時還會參與到函數的重載中,即經過const對象、const指針或引用調用方法時,優先調用const方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

class A {

public:

    int &operator[](int i) {

        ++cachedReadCount;

        return data[i];

    }

    const int &operator[](int i) const {

        ++size;

        --size;

        ++cachedReadCount;

        return data[i];

    }

private:

    int size;

    mutable cachedReadCount;

    std::vector<int> data;

};

A &a = ...;

const A &ca = ...;

int i = a[0];

int j = ca[0];

a[0] = 2;

ca[0] = 2;

這個例子中,若是兩個版本的operator[]有着基本相同的代碼,能夠考慮在其中一個函數中去調用另外一個函數來實現代碼的重用(參考Effective C++)。這裏咱們只能用非const版本去調用const版本。

1

2

3

int &A::operator[](int i) {

    return const_cast<int &>(static_cast<const A &>(*this).operator[](i));

}

其中爲了不調用自身致使死循環,首先要將*this轉型爲const A &,可使用static_cast來完成。而在獲取到const operator[]的返回值後,還要手動去掉它的const,可使用const_cast來完成。通常來講const_cast是不推薦使用的,但這裏咱們明確知道咱們處理的對象實際上是非const的,那麼這裏使用const_cast就是安全的。

constexpr

constexpr是C++11中新增的關鍵字,其語義是「常量表達式」,也就是在編譯期可求值的表達式。最基礎的常量表達式就是字面值或全局變量/函數的地址或sizeof等關鍵字返回的結果,而其它常量表達式都是由基礎表達式經過各類肯定的運算獲得的。constexpr值可用於enum、switch、數組長度等場合。

constexpr所修飾的變量必定是編譯期可求值的,所修飾的函數在其全部參數都是constexpr時,必定會返回constexpr。

1

2

3

4

5

6

7

constexpr int Inc(int i) {

    return i + 1;

}

constexpr int a = Inc(1);

constexpr int b = Inc(cin.get());

constexpr int c = a * 2 + 1;

constexpr還能用於修飾類的構造函數,即保證若是提供給該構造函數的參數都是constexpr,那麼產生的對象中的全部成員都會是constexpr,該對象也就是constexpr對象了,可用於各類只能使用constexpr的場合。注意,constexpr構造函數必須有一個空的函數體,即全部成員變量的初始化都放到初始化列表中。

1

2

3

4

5

6

7

struct A {

    constexpr A(int xx, int yy): x(xx), y(yy) {}

    int x, y;

};

constexpr A a(1, 2);

enum {SIZE_X = a.x, SIZE_Y = a.y};

constexpr的好處:

  1. 是一種很強的約束,更好地保證程序的正確語義不被破壞。
  2. 編譯器能夠在編譯期對constexpr的代碼進行很是大的優化,好比將用到的constexpr表達式都直接替換成最終結果等。
  3. 相比宏來講,沒有額外的開銷,但更安全可靠。

 

原文連接:http://www.cnblogs.com/fuzhe1989/p/3554345.html

相關文章
相關標籤/搜索