Item 29:異常安全を求めるコードEffective C++メモ

10351 ワード

Item 29: Strive for exception-safe code.
異常安全とは,異常が発生した場合,1)資源が漏洩しないこと,2)システムが不一致にならないことをいう.通常、3つの例外セキュリティレベルがあります.基本保証、強い保証、例外を捨てない保証です.
基本保証.例外が投げ出された後も、オブジェクトは正当な状態にあります.しかし、どの状態にあるかは不明です.
強く保証する.異常が投げ出された場合、プログラムの状態は何も変化しません.この関数を呼び出さなかったように.
異常保証を投げない.これは最強の保証で、関数はいつも約束したことを完成することができます.
例外を投げ出すシーン
メニュークラスが実装され、バックグラウンドピクチャを設定し、バックグラウンドカウントを切り替え、スレッドのセキュリティを提供できます.
class Menu{
    Mutex m;
    Image *bg;
    int changeCount;
public:
    void changeBg(istream& sr);
};
changeBgは、背景画像を変更するために使用され、このように実現される可能性がある.
void Menu::changeBg(istream& src){
    lock(&mutex);
    delete bg;
    ++ changeCount;
    bg = new Image(src);
    unlock(&mutex);
}

C++はCから受け継がれているので、投げ異常を完全に避けることは不可能です.たとえば、申請メモリは常に失敗する可能性があります.メモリが足りないと"bad alloc"の異常が放出されます.new Image(src)を加えて異常を投げ出すと、異常安全の2つの条件が破壊されます.mutexリソースが流出しました.unlockにはされていません. Menuデータ整合性が破壊された.まずbgが空になり、それからchangeCountも誤って自増した.
強い保証を提供する
通常、強い保証を提供するのは難しくありません.まず、私たちはリソースをスマートポインタに入れます.通常、shared_ptrauto_ptrよりも直感に合っています.これにより、リソースが漏洩されないことを保証できます(Item 13:使用対象によるリソース管理を参照).++changeCountの位置を再調整して、異常発生後もオブジェクトが一致することを保証します.
良い状態変更ポリシーは、あること(例えば背景変更)がすでに発生した場合にのみ、ある状態を変更してそれが発生したことを示すことです.
class Menu{
    shared_ptr<Image> bg;
    ...
};
void Menu::changeBg(istream& src){
    Lock m1(&m);
    bg.reset(new Image(src));
    ++changeCont;
}

インテリジェントポインタのresetは、その中のリソースをリセットするために使用され、古いリソースが呼び出されたdeleteである.このとき、new Imageに異常が発生した場合、reset関数には入らず、deleteも呼び出されない.実際、上記のコードは、Image構造関数でistream& srcのリードポインタを移動して異常を放出するなど、完璧な強い保証を提供することはできません.システムは変更された状態にあります.これはシステム全体に対する副作用であり、同様の副作用にはデータベース操作も含まれている.データベース操作を取り消す一般的な方法はないからだ.しかし、この点は無視できます.私たちはしばらくそれが完璧な強い保証を提供していると思っています.
copy&swapパターン"copy and swap"という設計ポリシーは、通常、異常な安全の強い保証を提供することができる.オブジェクトを変更する場合は、まずコピーしてからコピーを変更し、変更してから元のオブジェクトと交換します.このプロセスをよりよく例示するために、Menuの実装を変更し、"pimpl idiom"を用いてその実装をMenuImplに配置する.
class Menu{
    ...
private:
    Mutex m;
    std::shared_ptr<MenuImpl> pImpl;
};
Menu::changeBg(std::istream& src){
    using std::swap;            //   Item 25
    Lock m1(&mutex);

    std::shared_ptr<MenuImpl> copy(new MenuImpl(*pImpl));
    copy->bg.reset(new Image(src));
    ++copy->changeCount;

    swap(pImpl, copy);
}

これにより、私たちの操作はcopyであり、現在のオブジェクトに影響を与えることはありません.変更してからswapドルしかありません.swapは、異常を放出しない異常なセキュリティレベルを提供するべきである.参照:Item 25:異常を投げないswapを実現することを考慮します."copy and swap"ポリシーを使用して、ステータス全体を変更するか、すべてのステータスを維持するかを実現できます.しかし、関数全体に強い異常なセキュリティ保証を提供することはできません.例:
void Menu::changeBg(istream& src){
    ...
    f1();
    f2();
}

他の関数呼び出し、例えば、f1()が強い保証を提供しないと、関数全体が強い保証を提供することはできない(changeBgf1によるリソース漏洩および不一致を修復できないため).したがって、1つの関数の異常なセキュリティレベルは、呼び出されたすべての関数の中で最もセキュリティレベルが低いものよりも高くありません.これも、なぜ私たちが自分の関数に強いセキュリティ保証を提供するのか、そうしないと、これらの関数の使用者はより高いセキュリティレベルを提供することができず、最終的にシステム全体が安全ではありません.
まとめ
異常セキュリティ保証(exception-safe guarantee)は関数インタフェースの一部であり、お客様にも見られます.他のインタフェース(関数名、パラメータなど)を設計するようにセキュリティ保証を設計する必要があります.
異常セキュリティ関数では、リソースの漏洩やデータ破壊は発生しません.異常セキュリティは3つのレベルに分けられ、その中で強い保証は実現しにくい. "copy and swap"設計ポリシーを用いて、強い保証を実現することができる.
あなたが呼び出した最低セキュリティレベルの関数は、現在の関数の最高セキュリティレベルを決定します.
明記しない限り、本ブログの文章はすべてオリジナルで、転載はリンク形式で本文の住所を明記してください.http://harttle.com/2015/08/27/effective-cpp-29.html