2
#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;
}

Running this program will output:

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.

Right before the runtime_error, the variable data_ has been fully created. Why is it that data_ destructor not called?

Also, how do I make sure memory does not leak in this case?

Dat Chu
  • 10,822
  • 13
  • 58
  • 82
  • What are you expecting? The destructor isn't being called because the `std::runtime_error` is not being caught. – Collin Dauphinee Apr 12 '11 at 16:57
  • I was hoping that since data_ construction is done, its destructor will be called automatically. How do I make sure allocated data is freed when an exception happens? – Dat Chu Apr 12 '11 at 17:01
  • If you throw a concrete exception from the constructor your scoped array may still have a reference. Ideally you would want to catch this exception and handle it rather than unwinding to an abort. – AJG85 Apr 12 '11 at 17:01
  • Actually my first comment was wrong, sorry. The problem is that you're throwing from a constructor. When you throw from a constructor, the object is not fully constructed, so it's destructor won't be called. – Collin Dauphinee Apr 12 '11 at 17:05

1 Answers1

2

I think the issue is that your exception is uncaught and is being handled by the terminate handler. Since there is no catch to handle the exception, there is no way for the compiler to know how much to "unroll". If you catch the exception, then destruction occurs. You can then of course re-throw it if you like, for example:

#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 &) {
        throw;
    }
}

Outputs the following:

$ ./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
Evan Teran
  • 87,561
  • 32
  • 179
  • 238