c++筆記之noexcept修飾符
轉載自:https://www.cnblogs.com/sword03/p/10020344.html
https://www.zhihu.com/question/30950837
一、
關鍵字noexcept是由c++11標準引入的,它是c++11的新特性(當然現在都已經14、17、19、20了)。
從c++11開始,我們能看到很多程式碼當中都有關鍵字noexcept的出現。比如下面就是std::initializer_list的預設建構函式,其中就是用了noexcept
constexpr initializer_list() noexcept:_M_array(0),_M_len(0) { }
該關鍵字告訴編譯器,函式中不會發生異常,這有利於編譯器對程式做更多的優化。
如果在程式執行的過程中,noexcept函式向外丟擲了異常(如果函式內部捕捉了異常並完成處理,這種情況不算丟擲異常),程式會直接終止,呼叫std::terminate( )函式。該函式內部會呼叫std::abort( )終止程式。
二、C++的異常處理
c++中的異常處理是在執行時,而不是在編譯時檢測的。為了實現執行時檢測,編譯器建立額外的程式碼,然而這會妨礙程式優化。
在實踐中一般兩種異常丟擲方式是常用的:
(1)一個操作或者函式可能會丟擲一個異常;
(2)一個操作或者函式不可能丟擲任何異常。
後面(2)這種方式在以往的C++版本中常用throw( )表示,在c++11已經被noexcept關鍵字替代掉了。
void swap(Type &x, Type &y) throw() //c++11之前
{
x.swap(y);
}
void swap(Type &x, Type &y) noexcept //c++11
{
x.swap(y);
}
三、有條件的noexcept
在第2節中單獨使用noexcept,表示其所限定的swap函式絕對不發生異常。然而使用方式可以更靈活,表明在一定條件下不發生異常。
void swap(Type &x, Type &y) noexcept(noexcept(x.swap(y))) { x.swap(y); }
它表示如果操作x.swap(y)不發生異常,那麼函式swap(Type &x, Type &y)一定不發生異常!
一個更好的示例是std::pair中的移動分配函式(move assignment),它表明,如果型別T1和T2的移動分配(move assign)過程中不發生異常,那麼該移動建構函式就不會發生異常:
pair& operator = (pair&& __p)
noexcept(__and_<is_nothrow_move_assignable<_T1>,
is_nothrow_move_assignable<_T2>>::value)
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
四、什麼時候該使用noexcept
使用noexcept表明函式或操作不會發生異常,會給編譯器更大的優化空間。然而,並不是加上noexcept就能提高效率,步子邁大了也容易扯著蛋。
以下情形鼓勵使用noexcept:
- 移動建構函式(move constructor)
- 移動分配函式(move assignment)
- 解構函式(destructor)。這裡提一句,在新版本的編譯器中,解構函式是預設加上關鍵字noexcept的。下面程式碼可以檢測編譯器是否給解構函式加上關鍵字noexcept。
#include<iostream>
//結構體定義
struct X
{
~X(){}
};
int main()
{
X x; //例項化一個結構體物件(本質與類例項化一樣)
// This will not fire even in GCC 4.7.2 if the destructor is
// explicitly marked as noexcept(true)
static_assert(noexcept(x.~X()), "Ouch!");
}
- 葉子函式(Leaf Function)。葉子函式是指在函式內部不分配棧空間,也不呼叫其他函式,也不儲存非易失性暫存器,也不處理異常。
最後強調一句,在不是以上情況或者沒把握的情況下,不要輕易使用noexception。
注:
移動分配函式,英文名move assignment,又稱為移動賦值函式(微軟官方翻譯),它是c++11之後才引入的新特性,與早期版本中的複製賦值函式對應。以 a = f() 為例,其中f()返回一個與a同類型的臨時變數(記為b),早期的複製賦值函式會將在a中分配記憶體空間,然後將b的資訊複製給a;在c++11以後,可以直接將臨時變數b中的記憶體指標直接傳遞給a,由於避免了多餘的記憶體分配操作,因此大大提高了程式效率。具體可以參考微軟官方的例子:https://docs.microsoft.com/zh-cn/cpp/cpp/move-constructors-and-move-assignment-operators-cpp?view=vs-2019
關於異常這邊,effective modern C++中的意思是,noexcept還更傾向於一種介面上的設計保證,一個函式如果不拋異常,那麼就是noexcept的。我覺得考慮清楚的話,應該鼓勵新增上noexcept。雖然執行起來會duang~duang~很有爆炸性。
noexcept is particularly valuable for the move operations, swap, memory
deallocation functions, and destructors.
引一句effective modern c++的話~