Я боролся с этой проблемой в течение 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 происходит на удалить [] errorMessage. Я не понимаю почему. errorMessage, кажется, правильно инициализирован.
В чем причина SIGABRT?
Спасибо!
Проблема здесь:
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");
}
// ....
}
Когда программа на C ++ выдает исключение и обрабатывается обработчиком исключений, этот обработчик catch
блок) не должен бросать другой исключение, потому что это потребовало бы обработки два исключения в то же время, что является мостом слишком далеко даже для C ++. Однако, если обработчик выдает исключение, наказание за нарушение std::terminate
, что, вероятно, переводит на SIGABRT, который вы видите.
Использование сложного класса, такого как вы определили, для создания исключений рискованно из-за запрета двойного исключения и не нужно: как указывал mfontanini, вы можете просто использовать std::runtime_error
,