1. 程式人生 > 其它 >c++筆記之noexcept修飾符

c++筆記之noexcept修飾符

技術標籤:C++c++

轉載自: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++的話~