1

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

全局异常.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:Catch by reference)“从技术上讲,即使你通过引用捕获异常,编译器仍然使用按值传递。这是因为catch永远不会将控制权返回给调用者,因此负责清理-向上”
  • 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块)不能抛出另一个异常,因为这将需要同时处理两个异常,这也是一个桥梁甚至对于 C++ 来说也是如此。但是,如果处理程序确实引发了异常,则对违规的惩罚是std::terminate,这可能会转换为您所看到的 SIGABRT。

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

于 2014-05-29T15:34:35.577 回答