相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤,
例外宣告
Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept
理由
如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散,
示例
//不可能拋出例外時
double compute(double d) noexcept
{
return log(sqrt(d <= 0 ? 1 : d));
}
這里,已知compute不會拋出例外,因為它僅由不會拋出例外的操作所組成, 通過將compute宣告為noexcept,讓編譯器和讀者獲得資訊,使其更容易理解和操作compute,
注解
許多標準庫函式都是noexcept的,這包括所有從C標準庫中“繼承”來的標準庫函式,C++標準庫隱含地為這些函式標上了noexcept,
示例
//不能接受拋出例外時
vector<double> munge(const vector<double>& v) noexcept
{
vector<double> v2(v.size());
// ... 做一些事 ...
}
這里的noexcept表明不希望或無法處理vector物件拋出例外的情形,認為記憶體耗盡是一種嚴重的設計錯誤(類比于硬體故障),因此希望當其發生時讓程式崩潰,
注解
在大多數程式中,大多數函式都會拋出例外(比如說,
它們可能使用new
,呼叫會拋出例外的函式,或者通過拋出例外
來報告失敗的庫函式),因此請勿隨意到處散布noexcept
而不考慮清楚例外是否可以被處理,
Item 2:C++11中解構式和記憶體釋放函式都被默認隱式地具備noexcept性質
理由
解構式(無論是用戶自定義的,還是編譯器自動生成的)不能失敗,若解構式試圖拋出例外來退出,這是一種設計錯誤,程式最好終止執行,
示例
struct A
{
~A() {throw 1;} //隱式地具備noexcept性質
}
struct B
{
~B() noexcept(false) {throw 2;} //noexcept(false),顯式地宣告為可以拋出例外
}
struct C
{
B b; //成員有noexcept(false)的解構式,同樣可以拋出例外
}
注解
如果程式員顯示地為解構式指定了noexcept,或者類的基類或成員有noexcept(false)的解構式,解構式就不會再保持默認值(比如這里的B和C),上例中的類A解構式被默認為noexcept(true),可以阻止例外的擴散,而B和C均可以拋出例外,
理由
delete函式常被解構式所呼叫,C++11默認將delete函式設定成noexcept,從而提高應用程式的安全性,
示例
void operator delete (void *) noexcept;
void operator delete (void *) noexcept;
Item 3:對于constexpr
函式,移動操作,swap
函式,應該加上noexcept
理由
constexpr
函式在運行時執行時可能拋出例外,因此可能需要對其中的一些使用noexcept
,
示例
//constexpr函式
constexpr int fac(int n) noexcept
{
return (n>1) ? n*fac(n-1) : 1;
}
理由
能夠拋出例外的移動操作將違反大多數人的合理假設,不會拋出例外的移動操作可以更高效地被標準庫和語言設施所利用,
示例
//移動操作
//正例
template<typename T>
class Vector
{
// ...
Vector(Vector&& a) noexcept :elem{ a.elem }, sz{ a.sz }
{
a.sz = 0;
a.elem = nullptr;
}
Vector& operator=(Vector&& a) noexcept
{
elem = a.elem;
sz = a.sz;
a.sz = 0;
a.elem = nullptr;
}
// ...
public:
T * elem;
int sz;
};
這些操作不會拋出例外,
//反例
template<typename T>
class Vector2
{
// ...
Vector2(Vector2&& a)
{
*this = a; // 直接利用復制操作
}
Vector2& operator=(Vector2&& a)
{
*this = a; // 直接利用復制操作
}
// ...
public:
T * elem;
int sz;
};
Vector2 不僅低效,而且由于向量的復制需要分配記憶體而使其可能拋出例外,
理由
swap廣泛地以假定永不失敗的方式被使用,而且如果存在可能失敗的 swap函式的話,程式也很難撰寫為可以正確作業,如果元素型別的 swap會失敗的話,標準庫的容器和演算法也無法正確作業,
示例
//swap函式
class Foo
{
// ...
public:
void swap(Foo& rhs) noexcept
{
m1.swap(rhs.m1);
std::swap(m2, rhs.m2);
}
private:
Bar m1;
int m2;
};
為呼叫者方便起見,可以在型別所在的相同命名空間中提供一個非成員的 swap 函式,
void swap(Foo& a, Foo& b)
{
a.swap(b);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/248.html
標籤:C++