Я добавлю еще один ответ, пытаясь собрать все воедино.
Проблема
Проблема заключается в следующем.Рассмотрим пример:
double division(int a, int b) {
if( b == 0 ) {
throw "Division by zero condition!";
}
return (a/b);
}
В другом блоке будет вызвана функция:
double c;
try
{
c =division(d,f)
}
catch( ExceptionName e ) {
...
}
Обычно в вызываемых функциях генерируются исключения (т. Е. Генерируется экземпляр) (division
inпример выше) но они перехватываются другими частями кода (прямой вызов функции или даже в более внешних функциях).Чтобы сделать экземпляр исключения доступным из того места, где оно было сгенерировано, в место, где оно было перехвачено, экземпляр копируется.
Копирование экземпляров исключений, по-видимому, выполняется с помощью конструктора назначения копирования (=
, чтобы очистить).Так как я не объявлял один, используется по умолчанию.Этот конструктор по умолчанию имеет следующее поведение с указателями: вместо копирования указанного значения он копирует само значение указателя (то есть адрес), так что теперь у меня есть другой экземпляр, указывающий на тот же адрес.
Предположим, исключениепроисходит в функции division
выше.Предположим, копия экземпляра выполнена.При возврате из функции исходный экземпляр уничтожается, потому что он находится в памяти стека, поэтому вызывается деструктор и указатель будет освобожден.Однако память указателя также разделяется новой копией.
Когда копия попытается использовать свой указатель, возникнет ошибка, поскольку она не является действительным указателем.В моем случае он использовался деструктором, который пытался free
его, но он уже был освобожден: отсюда ошибка.
Правило 5
Проблема возникла из-за моего нарушенияправило 5 в C ++ 11 (ранее правило 3).
Правило гласит, что когда ресурс используется и управляется вручную (в моем случае память была ресурсом) и если класс реализует одиниз следующих членов также должны быть переопределены другие:
- деструктор
- конструктор копирования
- оператор назначения копирования
- конструктор перемещения копии
- оператор присвоения ходов
4 и 5 соответствуют 2 и 3, но с rvalues
( здесь хороший пост на lvalues
rvalues
)
Возможные решения
Самый простой способ - использовать string
, как уже указано в вопросе, комментариях и ответах, так что память автоматически управляется строковым классом.
Другая альтернатива (как опубликовано в другойответ) заключается в использовании общего указателя.
Хотя неудобно, чтобы быть согласованным с вопросом, здесь реализация, использующая чистые указатели.Конструкторы и деструктор переопределяются так, чтобы им выделялась новая память в конструкторах копирования, вместо этого использовалась память, выделенная другим экземпляром.
/* Define the exception here */
class BadLengthException: public exception
{
public:
BadLengthException(int strLength)
{
cout<<"Constructor\n";
strLen = strLength;
res = (char*)malloc(strLen+1);
int resultSize = sprintf(res, "%d", strLen);
}
/*assignment operator*/
BadLengthException& operator= (const BadLengthException &other)
{
cout<<"copy assignment constructor"<<endl;
if(&other == this)
{
return *this;
}
strLen = other.strLen;
res = (char*)malloc(strLen+1);
int resultSize = sprintf(res, "%d", strLen);
return *this;
}
/*copy constructor*/
BadLengthException(BadLengthException& other)
{
cout<<"copy constructor\n";
*this = other;
}
BadLengthException(BadLengthException&& other)
{
cout<<"move constructor "<<endl;
*this = other;
}
BadLengthException& operator=(BadLengthException&& other)
{
cout<<"move assignment operator"<<endl;
if(&other == this)
{
return *this;
}
*this = other;
return *this;
}
/*class destructor*/
~BadLengthException() throw()
{
cout<<"destructor"<<endl;
free(res);
}
virtual const char* what() const throw()
{
return res;
}
private:
int strLen;
char* res;
};
Пример для проверки класса:
int main(int argc, char *argv[])
{
{
cout<<"-----Expecting copy constructor------\n";
BadLengthException e(10);
BadLengthException e1(e);
cout<<"10: "<<e1.what()<<endl;
}
cout<<endl<<endl;
{
cout<<"-----Expecting copy assignment operator------\n";
BadLengthException e(10);
BadLengthException e2 = e;
cout<<"10: "<<e2.what()<<endl;
}
cout<<endl<<endl;
{
cout<<"-----move assignment operator------\n";
BadLengthException e3(1);
e3 = BadLengthException(33);
}
{
cout<<"-----move constructor------\n";
BadLengthException e4 = BadLengthException(33);
}
{
cout<<"-----move constructor------\n";
BadLengthException e5(BadLengthException(33));
}
cout<<"-----6------\n";
BadLengthException e6(1), e6_1(2);
e6 = std::move(e6_1);
return 0;
}