9

コンストラクターで例外をスローできるクラスがあります。適切なスコープで使用できるようにしながら、try / catchブロックでそのクラスのインスタンスを宣言するにはどうすればよいですか?

try { MyClass lMyObject; }
catch (const std::exception& e) { /* Handle constructor exception */ }

lMyObject.DoSomething(); // lMyObject not in scope!

RAIIイディオムを尊重しながら、これを達成するための代替方法はありますか?

init()二相工法は使いたくない。私が思いついた他の唯一のことは:

MyClass* lMyObject;

try { lMyObject = new MyClass(); }
catch (const std::exception& e) { /* Handle constructor exception */ }

std::shared_ptr<MyClass> lMyObjectPtr(lMyObject);
lMyObjectPtr->DoSomething();

正常に動作しますが、スコープ内の生のポインターとポインターの間接参照には満足していません。これは単なる別のC++いぼですか?

4

5 に答える 5

5

コンストラクターがスローした場合、それはオブジェクトの初期化に失敗したため、オブジェクトの存在を開始できなかったことを意味します。

MyClass* lMyObject;
try { lMyObject = new MyClass(); }
catch (std::exception e) { /* Handle constructor exception */ }

上記では、コンストラクターが例外をスローした場合、lMyObject初期化されないままになります。つまり、ポインターに不確定な値が含まれます。

詳細な説明については、従来のコンストラクターの失敗を参照してください。

C++コンストラクタモデルを次のように要約します。

また:

(a)コンストラクターは、そのendまたはreturnステートメントに到達することによって正常に戻り、オブジェクトが存在します。

または:

(b)コンストラクターは例外を発行して終了し、オブジェクトは現在存在しないだけでなく、存在しませんでした。

他の可能性はありません。

于 2013-02-05T15:02:02.743 に答える
2

コードを書く最良の方法はこれです:-

MyClass lMyObject; 
lMyObject.DoSomething(); 

試行、キャッチ、またはポインターはありません。

コンストラクターがスローした場合、DoSomethingを呼び出すことはできません。どちらが正しいか:コンストラクターがスローした場合、オブジェクトは作成されませんでした。

そして、重要なことに、例外をキャッチ(またはキャッチ/再スロー)しないでください。ただし、それらに建設的な関係がある場合を除きます。例外にその仕事をさせ、それらを処理する方法を知っている何かがその仕事をすることができるまで波及させます。

于 2013-02-05T15:43:02.230 に答える
1

コンストラクターは、オブジェクトを一貫性のある状態にし、クラスの不変条件を確立するためのものです。コンストラクターをエスケープする例外を許可するということは、そのコンストラクター内の何かが完了しなかったことを意味します。そのため、オブジェクトは未知の奇妙な状態になります(これにはリソースリークも含まれる可能性があります)。オブジェクトのコンストラクターが完了していないため、コンパイラーはオブジェクトのデストラクタを呼び出さないようにします。おそらくあなたが探しているのは、コンストラクター内の例外をキャッチすることです。再スローされないと仮定すると、これによりコンストラクターが実行を完了し、オブジェクトが完全に形成されます。

于 2013-02-05T15:34:09.547 に答える
0

使用する必要はありませんshared_ptr、使用してunique_ptrください:

std::unique_ptr<MyClass> pMyObject;
try { pMyObject.reset(new MyClass()); }
catch (std::exception &e) { /* Handle constructor exception */ throw; }
MyClass &lMyObject = *pMyObject;

lMyObject.DoSomething();

明らかに、関数を初期化するか、関数を終了する(たとえば、またはを介して)ことなく、プログラムがcatchブロックを通過しないようにするのはユーザーの責任です。pMyObjectreturnthrow

可能な場合は、Boost.Optionalを使用して、ヒープメモリの使用を回避できます。

boost::optional<MyClass> oMyObject;
try { oMyObject.reset(MyClass()); }
catch (std::exception &e) { /* Handle constructor exception */ throw; }
MyClass &lMyObject = *oMyObject;

lMyObject.DoSomething();
于 2013-02-05T15:07:09.380 に答える
-1

ガベージ入力を受け入れるようにMyClassのコピーコンストラクターを設定して、オブジェクトの宣言でポインターを効果的に宣言することができます。次に、tryブロック内でデフォルトのコンストラクターを手動で呼び出すことができます。

MyClass lMyObject(null); // calls copy constructor
try {
    new (lMyObject) MyClass(); // calls normal constructor
}
catch (const std::exception& e) { /* Handle constructor exception */ }

lMyObject.DoSomething();
于 2013-02-05T15:23:20.693 に答える