SIGABRT在异常类的析构函数中

时间:2012-11-25 15:41:31

标签: c++ exception destructor sigabrt throw

我正在与这个问题作斗争2天。我有一个解决方法,但我想了解更多的事情。让我们开始吧。 我有一个非常原始的异常类,它包含一个错误消息作为指向字符数组的指针(我知道std :: string的利润)。我知道“三个规则”所以它看起来像:

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();
};

globalexceptions.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:按引用捕获)“从技术上讲,即使通过引用捕获异常,编译器仍然使用pass by value。这是因为catch永远不会将控制返回到调用者,因此负责清理“
  • 确定:具有复制构造函数)“抛出的对象必须具有可公开访问的复制构造函数。允许编译器生成任意次数复制抛出对象的代码,包括零但是,即使编译器从未实际复制抛出的对象,它也必须确保异常类的复制构造函数存在并且可以访问“

根据GDB,不调用复制构造函数。 SIGABRT发生在 delete [] errorMessage 上。我不明白为什么。 errorMessage似乎已正确初始化。

SIGABRT的原因是什么?

谢谢!

2 个答案:

答案 0 :(得分: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");
    }
    // ....
}

答案 1 :(得分:1)

当一个C ++程序抛出异常并且它正由异常处理程序处理时,该处理程序(catch块)必须抛出另一个例外,因为这需要同时处理两个异常,这对于C ++来说太过分了。但是,如果处理程序确实抛出异常,则违规的惩罚是std::terminate,这可能会转换为您正在看到的SIGABRT。

使用复杂的类(例如您为抛出异常而定义)是有风险的,因为禁止双重例外,并且是不必要的:正如mfontanini指出的那样,您可以使用std::runtime_error