我正在与这个问题作斗争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>
我做了一些研究,并找到了以下信息和建议:
根据GDB,不调用复制构造函数。 SIGABRT发生在 delete [] errorMessage 上。我不明白为什么。 errorMessage似乎已正确初始化。
SIGABRT的原因是什么?
谢谢!
答案 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
。