1

私はこの問題と2日間戦っていました。回避策はありますが、さらに何が起こるかを理解したいです。それでは始めましょう。エラーメッセージを文字配列へのポインタとして保持する非常にプリミティブな例外クラスがあります(std::stringの利益について知っています)。「3つのルール」を知っているので、次のようになります。

globalexceptions.hpp

class FatalError {
    public:
        const char* errorMessage;
        /* WARNING:
         * "Rule of three". You see it below.
         */
        FatalError(const char* pErrorMessage);
        FatalError(const FatalError& rhs);
        FatalError& operator=(const FatalError& rhs);
        ~FatalError();
};

グローバル例外.cpp

FatalError::FatalError(const char *pErrorMessage):
    errorMessage(pErrorMessage)
{}

FatalError::FatalError(const FatalError& rhs){
    char* buf = new char[strlen(rhs.errorMessage)+1];
    strcpy(buf, rhs.errorMessage);
    errorMessage = buf;
}

FatalError& FatalError::operator =(const FatalError& rhs){
    if (this == &rhs)
        return *this;
    delete[] errorMessage;
    char* buf = new char[strlen(rhs.errorMessage)+1];
    strcpy(buf,rhs.errorMessage);
    errorMessage = buf;
    return *this;
}

FatalError::~FatalError(){
    delete[] errorMessage;
}

しかし、投げるとき:

int Config::readConfig(int argc_p, char *argv_p[])
{
    if ( argc_p != 2 )
    {
        throw FatalError ("Sick usage. Try: <file.ini>\n");
    }

「SIGABRT」を取得します。

いくつかの valgrind 分析:

Invalid free() / delete / delete[] / realloc()
  in FatalError::~FatalError() in globalexceptions.cpp:26
Address 0x413980 is not stack'd, malloc'd or (recently) free'd  1: operator delete[](void*) in /tmp/buildd/valgrind-3.7.0/coregrind/m_replacemalloc/vg_replace_malloc.c:490
  2: FatalError::~FatalError() in <a href="file:///home/gumba/Projects/cpp/backup_helper/backup_helper-build-desktop-Qt_4_8_2_in_PATH__System__Debug/../backup_helper/globalexceptions.cpp:26" >globalexceptions.cpp:26</a>
  3: /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
  4: main in <a href="file:///home/gumba/Projects/cpp/backup_helper/backup_helper-build-desktop-Qt_4_8_2_in_PATH__System__Debug/../backup_helper/main.cpp:35" >main.cpp:35</a>

私はいくつかの調査を行い、次の情報とアドバイスを見つけました。

  • ( OK: 参照によるキャッチ) 「技術的には、参照によって例外をキャッチした場合でも、コンパイラは値渡しを使用します。これは、キャッチが呼び出し元に制御を返さないためです。上"
  • ( OK: コピー コンストラクターを使用) 「スローされるオブジェクトには、パブリックにアクセス可能なコピー コンストラクターが必要です。コンパイラーは、スローされたオブジェクトを何度でもコピーするコードを生成できます (ゼロを含む)。ただし、コンパイラーが実際にコピーしない場合でも、例外クラスのコピー コンストラクターが存在し、アクセス可能であることを確認する必要があります。」

GDB によると、コピー コンストラクターは呼び出されません。SIGABRT はdelete[] errorMessageで発生します。理由がわかりません。errorMessage は適切に初期化されているようです。

SIGABRTの理由は何ですか?

ありがとう!

4

2 に答える 2

4

問題はここにあります:

FatalError::FatalError(const char *pErrorMessage):
    errorMessage(pErrorMessage)
{}

それを保存するだけでなくconst char*、メンバーに十分なサイズを割り当てて、errorMessageそこにコピーする必要があります。そうしないと、デストラクタでdelete文字列リテラルのアドレスを指定することになり、未定義の動作につながります。

とにかく、ここではポインターを使用しないでください。std::stringメモリを処理する を使用するだけです。

class FatalError {
public:
    FatalError(const char *msg) : errorMessage(msg) {}
    // Add an overload for std::strings
    FatalError(const std::string &msg) : errorMessage(msg) {}

    std::string errorMessage;
};

コピー代入演算子、コピー コンストラクタ、デストラクタを実装する必要はありません。

std::runtime_errorこれは、実装したものとまったく同じで、標準ライブラリで提供されています。

#include <stdexcept>

int Config::readConfig(int argc_p, char *argv_p[])
{
    if ( argc_p != 2 )
    {
        throw std::runtime_error("Sick usage. Try: <file.ini>\n");
    }
    // ....
}
于 2012-11-25T15:47:42.220 に答える
1

C++ プログラムが例外をスローし、それが例外ハンドラーによって処理されている場合、そのハンドラー (catchブロック)は別の例外をスローしてはなりません。これは、 2 つの例外を同時に処理する必要があるためです。これはブリッジでもあります。 C ++でもはるかに。ただし、ハンドラーが例外をスローした場合、違反に対する罰は です。これはおそらく、表示されている SIGABRT に変換されます。std::terminate

例外をスローするために定義したような複雑なクラスを使用することは、二重例外が禁止されているため危険であり、不必要です.mfontaniniが指摘したように、std::runtime_error.

于 2014-05-29T15:34:35.577 に答える