SIGABRT в деструкторе класса исключения

Я боролся с этой проблемой в течение 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?

Спасибо!

1

Решение

Проблема здесь:

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");
}
// ....
}
4

Другие решения

Когда программа на C ++ выдает исключение и обрабатывается обработчиком исключений, этот обработчик catch блок) не должен бросать другой исключение, потому что это потребовало бы обработки два исключения в то же время, что является мостом слишком далеко даже для C ++. Однако, если обработчик выдает исключение, наказание за нарушение std::terminate, что, вероятно, переводит на SIGABRT, который вы видите.

Использование сложного класса, такого как вы определили, для создания исключений рискованно из-за запрета двойного исключения и не нужно: как указывал mfontanini, вы можете просто использовать std::runtime_error,

1

По вопросам рекламы [email protected]