1. 程式人生 > 程式設計 >一文讓你徹底明白C++中的const

一文讓你徹底明白C++中的const

在抽象的最高層次上,const做兩件事:

* 一種保護你自己的方式(類似於private)

* 對編譯器的一種指示,表明標記為const的物件適合於程式的資料段。換句話說,屬於只讀資料(ROM-able)。

可以通過例子來看下const的應用。第一個例子中,使用const覆蓋了整個例子:

void fun(int i,std::string const & str)
{
 i = 0;     //ok.
 str = "";    //error!
 int const n = 42;
 n = 2;     //error!
}

第二種情況只適用於靜態初始化的名稱空間-作用域變數(又稱全域性變數):

int const pi = 3; //ROM-able
std::vector<int> const ivec = {/* ... */}; //Not ROM-able,might allocate.

對宣告為const的變數的任何寫操作都被顯示為未定義行為。這支援const全域性變數在ROM中的位置。

如果一個變數定義在ROM中,對它的寫操作很可能會使程式崩潰,這取決於平臺。如果一個變數不在ROM中,對它的寫操作只會改變它的值。這兩種情況的結合就是為什麼對const變數執行寫操作的行為是未定義行為而不是錯誤。

如果真的需要改寫一個const變數的值,可以通過`const_cast`來改寫:

void fun(int i,std::string const & str)
{
 i = 0; //ok.
 const_cast<std::string &>(str) = ""; //Also ok (maybe).
}

然而,const_cast並不能避免你在嘗試寫入宣告為const的變數時永遠不會遭遇未定義行為的陷阱。

std::string str = "";
fun(0,str); // Ok.
std::string const const_str = "";
fun(0,const_str); // Undefined Behavior!!

因此,只有在確實需要時才使用const_cast,並且只有在知道要寫入的底層變數是如何宣告的情況下才使用。

**那麼,究竟在什麼時候什麼地方使用const?**

答案就是**Everywhere**。將每個變數宣告為const,除非您知道它將被寫入。更一般地,在編譯器接受的任何地方新增const。

int foo(int arg)
{
 int const x = compute_intermediate_result(arg);
 int const y = compute_other_intermediate_result(x);
 return something_computed_from(x,y);
}

優化器視角下的const

為了優化目的,編譯器通常不能使用一致性進行優化。

int get_value(some_class const & x,int const at)
{
 int offset = compute_offset(at);
 return x[offset];
}

此時,在這些函式引數中使用const對優化器沒有幫助。x上的const不起作用,因為x已經通過引用傳遞了。沒有x的副本,編譯器不知道x是否宣告為const。在引數at上的const不能幫助我們,因為at是一個拷貝,它可以以任何方式裝入暫存器。
如果編譯器可以看到const物件的宣告,它有時可以使用其一致性進行優化。

std::vector<int> const vec = { 1,2,3 };

int main()
{
 // This may generate code that indexes into vec,or it may generate
 // code that loads an immediate 2.
 return vec[1];
}

如果您想保證這樣的優化,您可以在c++11或以後的版本中使用constexpr。使用constexpr宣告的變數只對可以靜態初始化的型別進行編譯,因此,如果編譯了它,就會得到一個ROM-able的物件。

constexpr std::array<int,3> arr = { 1,3 };

int main()
{
 // This generates code that loads an immediate 2 on every
 // compiler I tried.
 return arr[1];
}

constexpr只處理字面常量型別(literal types)。這些型別與你可能在C中找到的型別相似。任何被宣告為const的int或其他整數值都可以像文字一樣使用。

void foo(int const arg)
{
 int const size = 2; 

 int array_0[2];  // Ok.
 int array_1[arg]; // Error! arg is a runtime value.
 int array_2[size]; // Ok.
}

靜態const類成員

如果宣告一個類成員static const,就很像宣告一個全域性const變數。

int const global_size = 3;

struct my_struct
{
 static int const size = 2;
};

std::array<int,global_size> make_global_array()
{ return {}; }

std::array<int,my_struct::size> make_my_struct_array()
{ return {}; }

但因為這是c++,所以有個問題。如果定義的static const整數值超越界限,則它可能無法被當作字面常量使用,例如一下的例子。

struct my_struct
{
 static int const size;
};

std::array<int,my_struct::size> make_my_struct_array() // Error!
{ return {}; }

int const my_struct::size = 2;

這是的錯誤時因為編譯器在解析foo()時不知道要為my_class::size使用什麼值。如果你希望像使用全域性const整數值一樣使用static const整數值,請始終將它們宣告為內聯(inline)。

總結

到此這篇關於C++中const的文章就介紹到這了,更多相關C++中的const內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!

1. Use const to protect yourself from silly mistakes,and use it everywhere.

2. You can use const on globals that you want in ROM,but prefer constexpr.

3. Use const_cast sparingly,or not at all.

4. When you do use const_cast,be careful of the UB that might result,including crashes.

5. Use const globals and stack variables instead of macros for named values that are "as good as literals".

6. Define static const integral data members inline,if possible.