C++ 异常类的析构函数中的SIGABRT

C++ 异常类的析构函数中的SIGABRT,c++,exception,destructor,sigabrt,throw,C++,Exception,Destructor,Sigabrt,Throw,我和这个问题斗争了两天。我有一个解决办法,但我想了解更多的情况。让我们开始吧。 我有一个非常原始的异常类,它保存一条错误消息,作为指向我所知道的关于std::string的好处的字符数组的指针。我知道三的法则,所以看起来像: globalexceptions.hpp globalexceptions.cpp 但在投掷时: int Config::readConfig(int argc_p, char *argv_p[]) { if ( argc_p != 2 ) {

我和这个问题斗争了两天。我有一个解决办法,但我想了解更多的情况。让我们开始吧。 我有一个非常原始的异常类,它保存一条错误消息,作为指向我所知道的关于std::string的好处的字符数组的指针。我知道三的法则,所以看起来像:

globalexceptions.hpp

globalexceptions.cpp

但在投掷时:

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

一些分析:

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:从技术上讲,通过引用捕获,即使通过引用捕获异常,编译器仍然使用传递值。这是因为catch从不将控制权返回给调用方,因此负责清理 确定:抛出的Have copy constructor对象必须具有可公开访问的副本构造函数。编译器可以生成复制抛出对象任意次数(包括零次)的代码。但是,即使编译器从未实际复制抛出的对象,它也必须确保异常类的复制构造函数存在并且可以访问 根据GDB,不调用复制构造函数。SIGABRT发生在delete[]errorMessage上。我不明白为什么。errorMessage似乎已正确初始化

SIGABRT的原因是什么

谢谢

问题在于:

FatalError::FatalError(const char *pErrorMessage):
    errorMessage(pErrorMessage)
{}
您不应该只存储const char*,应该在errorMessage成员中分配足够的大小,并将其复制到那里。否则,在析构函数中,您将删除字符串文本的地址,这将导致未定义的行为

无论如何,你不应该在这里使用指针。只需使用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");
    }
    // ....
}
问题在于:

FatalError::FatalError(const char *pErrorMessage):
    errorMessage(pErrorMessage)
{}
您不应该只存储const char*,应该在errorMessage成员中分配足够的大小,并将其复制到那里。否则,在析构函数中,您将删除字符串文本的地址,这将导致未定义的行为

无论如何,你不应该在这里使用指针。只需使用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");
    }
    // ....
}

<>当一个C++程序抛出一个异常,并且它由异常处理程序处理时,处理程序catch块不能抛出另一个异常,因为这将需要同时处理两个异常,即使是C++,也是一个过长的桥梁。但是,如果处理程序确实抛出异常,则对违规行为的惩罚是std::terminate,这可能会转化为您看到的SIGABRT


使用一个复杂的类,如您为抛出异常定义的,是有风险的,因为双重例外禁止,以及不必要的:正如MFANTANNI指出的,当C++程序抛出异常并由异常处理程序处理时,可以只使用STD::RunTimeOrth.Errase/< P> < P>那个处理程序的catch块不能抛出另一个异常,因为这将需要同时处理两个异常,这是一个甚至对于C++来说太远的桥梁。但是,如果处理程序确实抛出异常,则对违规行为的惩罚是std::terminate,这可能会转化为您看到的SIGABRT


使用复杂的类(如您定义的用于抛出异常的类)是有风险的,因为双重异常禁止,而且是不必要的:正如mfontanini指出的,您可以只使用std::runtime\u error。

使用std::string,并且您不需要三条规则:通常在运行时中止时,它还向stderr或stdout打印错误消息。但听起来很可能您有双免费使用std::string,并且不需要三条规则:通常,当运行时中止时,它还会向stderr或stdout打印错误消息。但听起来你很可能有双重自由