2016-07-20 10 views
2

私自身の例外を派生しています。MyExceptionstd::system_errorから電話し、メッセージを計算して返すためにwhat()をオーバーライドしました。 MyExceptionの初期化子リストは、メッセージを受け取るsystem_errorコンストラクタオーバーライドを呼び出さない。std :: exceptionは何を所有していますか?

私はMyExceptionをキャッチし、std::exceptionstd::exceptionwhat()を呼び出した結果を、それをコピーする場合はnullptrです。意味あり。

MyExceptionの初期化時にメッセージを受け取るsystem_exceptionのコンストラクタを使用すると、system_errorがメッセージのコピーを取り、それを解放することが指定されていますか?

私はstd::exceptionMyExceptionのコピーが有効なwhat()を返すことができると仮定しています。 MyExceptionsの新しいものが作成されるたびに計算する必要があるという点でパフォーマンス・ヒットを取りますが、 what()が最初に呼び出されたときにだけ、それを怠惰に計算することはできません。

what()char*を返し、const std::string&を返さないため、「what」文字列の所有権について少し気になります。

class MyException : public std::system_error 
    { 
     std::string what_; 
    public: 
     MyException(int errorValue, const std::error_category& category) 
      : std::system_error(errorValue, category) 
     {} 

     char* what() const 
     { 
      what_ = "MyException: " + to_string(code().value()); 
      return what_.c_str(); 
     } 
    }; 

    int main() 
    { 
     std::exception ex; 

     try 
     { 
      throw MyException(4, system_category()); 
     } 
     catch(const MyException& e) 
     { 
      ex = e; 
     } 

     printf("what= %s", ex.what()); 

     return 1; 
    } 
+0

コードを表示してください。そして、「MyExceptionを捕捉してstd :: exceptionにコピーすると、例外オブジェクトがスライスされます。それが呼び出されるまで「what」の結果が存在しない可能性があります。 –

+0

@RichardCrittenコードを追加しました。私はスライスを理解しています。 –

+1

'std :: runtime_error'は文字列を所有しています。具体的には、 'std :: string'ではなく、変更不可能なref-counted文字列です。 – ildjarn

答えて

6

私の質問は、それはそれSYSTEM_ERRORは、メッセージのコピーを取り、それを所有し、それを解放します指定されていますか?

はい、これは標準で保証されています。 std::runtime_errorない -

開始するには、std::exceptionwhatが所有していません。

runtime_error(const string& what_arg); 

効果:クラスruntime_errorのオブジェクトを構築std::runtime_errorのコンストラクタは([runtime.error] P2-5)thusly定義されています。
ポストコンディション:strcmp(what(), what_arg.c_str()) == 0

runtime_error(const char* what_arg); 

効果:クラスruntime_errorのオブジェクトを構築し
事後条件:strcmp(what(), what_arg) == 0。だから、

に渡された値の寿命についての要件は存在しないとして、それは、内部what_argコピーを格納する必要があり

を次の[例外]はありますP2:。

クラスexceptionから派生する各標準ライブラリクラスTは、公にアクセス可能なコピーコンストラクタと、例外なく終了しない公的にアクセス可能なコピー代入演算子を持たなければならない。これらのメンバー関数は、次の事後条件を満たさなければならない。lhsrhsの両方のオブジェクトがTlhsの動的タイプを持つ場合、rhsのコピーである場合、strcmp(lhs.what(), rhs.what())0に等しくなります。

したがって、コピーコンストラクタが必要です。スローしないでください。コピーは、what()の同じ戻り値を維持する必要があります。コピー代入演算子の場合も同様です。すべて一緒にこれを置く

、我々はstd::runtime_errorは(コピーするときに割り当てからの例外を避けるために)あなたは、文字列を参照カウントで、内部what_argために渡した値を保持しなければならないことを推測することができ、その値にかかわらず、コピーおよび/またはスライスの持続します - ただし、std::runtime_errorstd::exceptionまで! std::runtime_errorから

std::system_error継承するので、すべて同じ、それ、そこから派生する任意のタイプ(のためにも当てはまる:(move constructor for std::runtime_errorwhatのストレージに関する根拠と要件の詳細については、@HowardHinnantによって、この非常に興味深い答えで見つけることができます)派生型が非投げ込みコピーコンストラクタを不変に維持する限り)。

私はこれが有効なwhat()を返すことができるようにMyExceptionstd::exceptionコピーを可能にすると仮定しています。

いいえ! std::exceptionMyExceptionにすると、オブジェクトはwhatの値が物理的に格納されているよりも少ない派生型になります。 の場合、例外のコピーを作成する必要があります。使用できる最小の派生型はstd::runtime_errorです。 (あなたが常に安全に当然のMyExceptionからstd::exception参照し、作ることができます。)別の言い方をすると、std::exceptionオブジェクトwhat()から意味のある文字列を取得するために決して可能です。私はそれが(明白な理由のために)割り当ての例外コンストラクタを書くために貧しいフォームだと言うでしょう

#include <cstdio> 
#include <stdexcept> 
#include <system_error> 
#include <string> 

class MyException : public std::system_error { 
public: 
    MyException(int errorValue, std::error_category const& category) 
     : std::system_error(
      errorValue, category, 
      "MyException: " + std::to_string(errorValue) 
     ) 
    { } 
}; 

int main() { 
    std::runtime_error ex; 

    try { 
     throw MyException(4, system_category()); 
    } catch(MyException const& e) { 
     ex = e; 
    } 

    std::printf("what= %s", ex.what()); 
} 

が、そのすべての現在の標準的な与えられた:


このコードは移植性、あなたが望む行動を持っています私が知っているライブラリの実装では、std::basic_string<>short-string optimizationを使用していますが、これは実際には問題になることはほとんどありません。

0

クラスの例外は任意の文字列を所有していない:

コードは、この(私はこれをコンパイルしていない)のようなものです。例外オブジェクトをスライスすると、what()仮想関数がオーバーライドされていない基本例外オブジェクトが取得されます。

what()関数の魔法は、仮想関数what()にあり、派生クラスにあります。静的メモリに格納されているconst char *を例外オブジェクトに渡すことができ、それはコピーされません。

オブジェクトをコピーすると、例外が発生する可能性があり、推奨されません。たとえば、bad_allocの後に新しい文字列オブジェクトを作成することはできません。このため、例外は値ではなく参照によってよく捕捉されます。

1

あなたの質問は、例外のライフサイクルを理解することに関連しています。その質問は投稿herehereで議論されており、参考になるかもしれません。

スマートポインタを使用して、例外の有効期間を延長することができます。どのようなパフォーマンスの影響があるのか​​よく分かりませんが、std::system_errorの拡張機能を利用して、コピーの作成を完全に避けるためにこれを使用できます。 (実際には、私はコピーの構築を避けることはできません。スマートポインタの作成は、例外をコピーするかどうかはわかりませんが、あなたの例外をコピーします。コピーコンストラクタ提供する必要があります)。あなたの主な機能は、このように見えるようになります。

#include <exception> // std::exception_ptr 

int main() 
{ 
    std::exception_ptr p; 

    try 
    { 
     throw MyException(4, system_category()); 
    } 
    catch(const MyException& e) 
    { 
     p = std::current_exception(); 
    } 

    try 
    { 
     std::rethrow_exception(p); 
    } 
    catch (const std::exception& e) 
    { 
     printf("what= %s", e.what()); 
    } 

    return 1; 
} 

これは基本的に私がcplusplus.com here上についてお読み例外ポインタの使用例のほんの書き換えですが、私はあなたの例外クラスではなく、std::logic_errorような標準的な例外を使用しました。

元の質問に関しては、厳しい保証をするのは難しいようです。 C++ 11に適用される例外の代入演算子のドキュメントで、次の文が見つかりました。C++ 98では、この保証さえ提供されていません。

少なくともC++標準ライブラリ(これを含む)内のすべての例外は、動的型が一致したときにmemberが返す文字列表現を保持するコピー代入演算子オーバーロードを少なくとも含みます。

しかし、std::system_errorの動的タイプは、あなたのケースでstd::exceptionのダイナミック型と一致しませんので、私は動作するように保証されるだろうとは思いません。私はMyExceptionを初期化するときにメッセージを取るSYSTEM_EXCEPTIONのコンストラクタを使用する場合

関連する問題