4

例外をスローするこのコンストラクターがあります

GenericSocket::GenericSocket(const string& hostname, 
                             const string& servname):
                             _hostname(hostname),
                             _servname(servname)
{  
    initHints();
    int rv;
    if((rv = getaddrinfo(_hostname.c_str(), 
                    _servname.c_str(), 
                    &_hints, 
                    &_servinfo)) != 0) {  
        throw GenericSocketException();
    }  

} 

initHints() は、_hints の memset を実行し、いくつかの変数を設定します。

次のようなGoogleテストフレームワークでテストします。

TEST(CreateObject2, getaddrinfoException)
{
    mgs_addrinfo_return = 1; 
    ASSERT_THROW(new GenericSocket("testhost", "4242"), GenericSocketException);
}

テストはコア ダンプで失敗します。

[ RUN      ] CreateObject2.getaddrinfoException
socket creation failed
terminate called after throwing an instance of 'common::GenericSocketException'
  what():  Socket creation failed
[1]    43360 abort (core dumped)  ./bin/test_common

何が問題なのか正確にはわからないという事実に加えて、初期化されていないオブジェクトが削除されているのではないかと疑っています (?)。オブジェクトの作成後に呼び出すことができる別の関数にこの機能を配置し、後で例外を処理する方がよいでしょうか?

4

6 に答える 6

11

IMHO、コンストラクターで例外をスローすることは、このような状況を処理するための最良の方法です-ソケットがない場合、本当に使用可能なオブジェクトが必要ですか? 私には意味がありません。そのアドレスの解決に失敗している場合、それには理由があり、それは例外に値します (正しく処理する限り!)

あなたの特定のケースでは、戻り値をテストし、例外をより便利にする必要があります...(たとえば、HostNotFoundここではそうだと思います)

于 2011-01-04T20:06:06.963 に答える
6

はい、そうです。実際には選択の余地がありません。コンストラクターには戻り値がありません。

ただし、例外の安全性に注意してください。たとえば、http://www.drdobbs.com/184403429を参照するか、Google の「強力な例外保証」を参照してください。実際、コンストラクターがスローしたオブジェクトは破棄されず (存在したことはありません)、リソースをリークしない状態のままにしておく必要があります。

于 2011-01-04T20:08:56.207 に答える
5

もちろん、オブジェクトを構築できない場合に行うべき唯一の合理的な方法は、例外をスローすることです。そうしないと、ゾンビ オブジェクトになってしまうことになります。他の質問に答えると、いいえ、作成されていないオブジェクトを破棄することはできません。

于 2011-01-04T20:08:41.317 に答える
4

はい、例外をスローすることは、値を返さないため、コンストラクターで問題が発生したことを報告する最も直接的な方法です。

一方、デストラクタは、例外処理のスタック巻き戻しフェーズで呼び出され、この時点で別の例外をスローすると中止されるため、例外をスローしないでください。

于 2011-01-04T20:48:13.183 に答える
1

構築時に有効なオブジェクトを作成できないことがわかった場合は、例外をスローすることがおそらく最善の選択肢です。したがって、クラスのユーザーは、エラーが発生した場合、それを処理できない限り続行することを禁じられています。これは通常、プログラムの正しい動作です。

function()
{
    // 1. acquire resources.
    // 2. perform action.
    // 3. release resources.
}

ステップ 1 を実行できない場合、他のステップは無駄です。それが私たちが RAII を使用する目的です。最初に必要なすべてのリソースを取得します。これは、スタック変数に関する古い C スタイルのプログラミングにいくぶん類似しています。RAII を使用すると、コンストラクターの例外によっていずれかのリソースの取得に失敗した場合、以前に取得したすべてのリソースが自動的に解放され、2 番目のステップが試行されることはありません。すべてが舞台裏で自動的に行われますが、これは、オブジェクトを作成できない場合にコンストラクターで例外をスローすることを前提としています。また、デストラクタがクリーンアップを行うことも想定しています。

struct Connection {
    Connection() {
        if( ! connect() ) throw ConnectionError();
    }
    ~Connection() {  // may not throw under any circumstances
        try { disconnect(); }
        catch( ... ) { }
    }
    void send(const std::string& message);
    // ...
private:
    bool connect();
    // ...
};

void post(const std::string& message)
{
    // step one is easy for the user:
    Connection c;
    // step two is the bulk of the work:
    c.send("HELO");
    // step three is automatically done for the user.
}
于 2011-01-04T21:15:15.210 に答える
1

これについては、すでに少なくとも 2 つの議論が SO で行われています。コンストラクターからの例外のスローと 2 フェーズの初期化の両方で:

1)コンストラクターが例外をスローするのはいつですか?

2)コンストラクターからの例外のスロー

于 2011-01-04T21:13:39.927 に答える