質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

5662閲覧

コンストラクタから例外を投げたときのdelete

BeatStar

総合スコア4958

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2016/05/16 03:53

編集2016/05/16 03:59

C/C++ でやっています。

インターフェースクラスを用意し、インターフェースクラスを継承してできた子クラスをFactoryMethodパターンで実体として生成します。

そのとき、この子クラスから例外が投げられるときのdeleteがうまくいきません。

たとえば、

  1. ITestクラスから継承してCTestクラスを定義
  2. CreateInstance関数 ( グローバル関数 ) で FactoryMethodパターンで CTestの実体を生成& ITest* として返す
  3. CTestクラスのコンストラクタでは、引数が指定データでなければ例外を投げる
  4. main関数等の呼び出し側で例外処理

という感じでやっています。

このとき、
例外が投げられないとき ( throw文を削除 ) と 例外が発生しないとき は普通に動くのですが、
例外が発生したときに 実行時エラーになります。

newを使わない方法で生成する場合はうまくいくようですが、
DLLから クラスオブジェクトを取得し操作する
という感じでやりたいのでnew/deleteを使う方法になります。
ですが、delete のところがなぜか実行時エラー。

サンプルでは、

  1. 例外を受け取る ( catch )
  2. cout << 3 << endl;
  3. delete で破棄
  4. cout << 4 << endl;

となっているので、

本来なら、

3
( デストラクタ起動 )
4

みたいに印字されるはずですが、
3
のところで止まってしまいます。 ( エラーになる。 )

解決方法ってないのでしょうか。

一応自分でも調べたのですが、「コンストラクタから例外を投げるな」、「コンストラクタで初期化失敗した場合、唯一の連絡手段は例外処理である」、「(例外処理が発生した場合)メモリの確保に対する破棄等はコンストラクタ内で行うべき」といろいろあり、わかりづらいです...

特に, インターフェースでnewで生成して、前処理等でなんらかのエラーがあった場合にコンストラクタから例外が投げられるときの
呼び出し側 ( main関数等 ) での書き方です。

C++

1#include<iostream> 2 3using namespace std; 4 5// インターフェースクラス 6class ITest{ 7 public: 8 virtual ~ITest(){} 9 virtual int func() = 0; 10}; 11 12// 実際に使いたいクラス 13class CTest : public ITest{ 14 private: 15 bool bCheck; 16 protected: 17 public: 18 CTest( bool bCheck = false ); 19 ~CTest(); 20 21 int func(); 22}; 23 24// 子クラスのコンストラクタ 25CTest::CTest( bool bCheck ){ 26 if( bCheck == false ) throw -1; 27 this->bCheck = bCheck; 28} 29 30// 子クラスのデストラクタ 31CTest::~CTest(){ 32 cout << "Destructor" << endl; 33} 34 35// 子クラスのメンバ関数 36int CTest::func(){ 37 return 100; 38} 39 40 41// FactoryMethod を利用して 生成する関数 42ITest* CreateInstance( bool bCheck ){ 43 return new CTest( bCheck ); 44} 45 46 47int main( int argc, char *argv[] ) 48{ 49 ITest* obj; 50 try{ 51 cout << 1 << endl; 52 obj = CreateInstance( false ); 53 cout << 2 << endl; 54 }catch(int n){ 55 cout << 3 << endl; 56 delete obj; // ここで実行時エラー. 57 cout << 4 << endl; 58 } 59 60 // 停止用 61 getchar();getchar(); 62return 0; 63} 64

[環境等]
言語: C/C++
知りたいこと: FactoryMethodパターンで子クラスを生成するとき、コンストラクタから例外が投げられたときのオブジェクト破棄する方法

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コンストラクタで例外を飛ばしたなら、インスタンスは生成されていません。
ですから、それをdeleteする必要はないししてはいけないです。ファクトリメソッドが生成失敗をどういう形で呼び出し元に伝えるかにもよりますが、catchしたら粛々と失敗情報を返却すればそれで十分です。

投稿2016/05/16 04:03

yuba

総合スコア5568

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2016/05/16 04:05

なるほど。 例外を投げるとインスタンス化失敗だからdeleteが使えないんですね。
episteme

2016/05/16 05:05

ただし、"作りかけ"の分はちゃんと後しまつすべし。 class foo { int* p1; int* p2; ... foo() { p1 = new int[a];  p2 = new int[b]; // ココで失敗したなら、delete[] p1 すべし } ...
BeatStar

2016/05/16 07:46

なるほど。 ありがとうございました。
yuba

2016/05/16 08:58

epistemeさんおっしゃるとおり「作りかけ」を正しく処分する必要があってここに解放漏れの発生するタネがありますので、それを嫌う人のポリシーが「コンストラクタから例外を投げるな」となります。 このポリシーは絶対的なものではなく、むしろ「コンストラクタから例外を投げるときには作りかけの解放を忘れるな」、いやむしろ「解放を人間の注意力でマネージしなくていいようスマートポインタで自動化していけ」という話になっていきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問