QScopedArrayPointer защищает мои данные, но они все еще протекают

#c #qt #memory-leaks #smart-pointers

#c #qt #утечки памяти #интеллектуальные указатели

Вопрос:

 #include <QScopedArrayPointer>
#include <QDebug>
#include <stdexcept>

class MyData{
public:
  MyData() {
    qDebug() << "Construct a data";
  }

  ~MyData() {
    qDebug() << "Delete a data";
  }

private:
  float internal_data_;
};

class MyClass{
  QScopedArrayPointer<MyData> data_;
public:
  MyClass(){
    data_.reset(new MyData[10]);

    throw std::runtime_error("Shit happens");
  }
};

int main(int argc, char *argv[])
{
    MyClass a_class;

    return 1;
}
  

Запуск этой программы приведет к:

 Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Construct a data 
terminate called after throwing an instance of 'std::runtime_error'
  what():  Shit happens
The program has unexpectedly finished.
  

Прямо перед runtime_error переменная data_ была полностью создана. Почему не вызывается data_ destructor?

Кроме того, как мне убедиться, что в этом случае не происходит утечки памяти?

Комментарии:

1. Чего вы ожидаете? Деструктор не вызывается, потому что std::runtime_error не перехватывается.

2. Я надеялся, что, поскольку построение data_ завершено, его деструктор будет вызван автоматически. Как мне убедиться, что выделенные данные освобождаются при возникновении исключения?

3. Если вы создадите конкретное исключение из конструктора, ваш ограниченный массив все еще может содержать ссылку. В идеале вы хотели бы перехватить это исключение и обработать его, а не разворачивать до прерывания.

4. На самом деле мой первый комментарий был неправильным, извините. Проблема в том, что вы создаете из конструктора. Когда вы создаете объект из конструктора, объект не полностью сконструирован, поэтому его деструктор не будет вызван.

Ответ №1:

Я думаю, проблема в том, что ваше исключение не перехвачено и обрабатывается обработчиком terminate. Поскольку нет catch способа обработать исключение, компилятор не может узнать, сколько нужно «развернуть». Если вы перехватываете исключение, происходит уничтожение. Затем вы, конечно, можете повторно запустить его, если хотите, например:

 #include <QScopedArrayPointer>
#include <QDebug>
#include <stdexcept>

class MyData{
public:
  MyData() {
    qDebug() << "Construct a data";
  }

  ~MyData() {
    qDebug() << "Delete a data";
  }

private:
  float internal_data_;
};

class MyClass{
  QScopedArrayPointer<MyData> data_;
public:
  MyClass(){
    data_.reset(new MyData[10]);

    throw std::runtime_error("Shit happens");
  }
};

int main(int argc, char *argv[]) {
    try {
        MyClass a_class;
    } catch (const std::runtime_error amp;) {
        throw;
    }
}
  

Выводит следующее:

 $ ./test2 
Construct a data 
Construct a data 
Construct a data                                                                                                                     
Construct a data                                                                                                                     
Construct a data                                                                                                                     
Construct a data                                                                                                                     
Construct a data 
Construct a data 
Construct a data 
Construct a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
Delete a data 
terminate called after throwing an instance of 'std::runtime_error'
  what():  Shit happens
Aborted