Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delete object if constructor throws an exception?

So we have a constructor that can throw an exception depending on the arguments passed to it, but we do not know how to delete the object if this occurs. Important part of the code:

try
{
    GameBase *gameptr = GameBase::getGame(argc, argv);
    if (gameptr == 0)
    {
        std::cout << "Correct usage: " << argv[PROGRAM_NAME] << " " << "TicTacToe" << std::endl;
        return NO_GAME;
    }
    else
    {
        gameptr->play();
    }
    delete gameptr;
}
catch (error e)
{
    if (e == INVALID_DIMENSION)
    {
        std::cout << "Win condition is larger than the length of the board." << std::endl;
        return e;
    }
}
catch (...)
{
    std::cout << "An exception was caught (probably bad_alloc from new operator)" << std::endl;
    return GENERIC_ERROR;
}

In the third line, GameBase::getGame() calls the constructor for one of the games derived from GameBase and returns a pointer to that game, and these constructors can throw exceptions. The question is, how can we then delete the (partial?) object pointed to by gameptr if this occurs? If an exception is thrown, we will exit the scope of gameptr because we leave the try block and cannot call delete gameptr.

like image 749
Kerry Avatar asked Mar 30 '16 07:03

Kerry


People also ask

What happens if a constructor throws an exception?

When throwing an exception in a constructor, the memory for the object itself has already been allocated by the time the constructor is called. So, the compiler will automatically deallocate the memory occupied by the object after the exception is thrown.

How can I handle a constructor that fails a throw exception?

[17.1] How can I handle a constructor that fails? Throw an exception. Constructors don't have a return type, so it's not possible to use error codes.

What happens if an exception is throws from an object's constructor and object's destructor?

When an exception is thrown, destructors of the objects (whose scope ends with the try block) are automatically called before the catch block gets executed. That is why the above program prints “Destructing an object of Test” before “Caught 10“.

Is it OK to throw exception in constructor in C++?

Yes, throwing an exception from the failed constructor is the standard way of doing this. Read this FAQ about Handling a constructor that fails for more information. Having a init() method will also work, but everybody who creates the object of mutex has to remember that init() has to be called.


1 Answers

To assess the exception safety, you need to provide more detail of the construction of the object in GameBase::getGame.

The rule is through, that if a constructor throws, the object is not created, hence the destructor is not called. Associated memory allocations are also deallocated (i.e. the memory for the object itself).

The issue then becomes, how was the memory allocated to begin with? If it was with a new GameBase(...), then there is no need to deallocate or delete the resultant pointer - the memory is deallocated by the runtime.


For clarity on what happens to the member variables that are already constructed; they are destructed on the exception of the "parent" object. Consider the sample code;

#include <iostream>
using namespace std;
struct M {
    M() { cout << "M ctor" << endl; }
    ~M() { cout << "M dtor" << endl; }
};
struct C {
    M m_;
    C() { cout << "C ctor" << endl; throw exception(); }
    ~C() { cout << "C dtor" << endl; }
};
auto main() -> int {
    try {
        C c;
    }
    catch (exception& e) {
        cout << e.what() << endl;
    }
}

The output is;

M ctor
C ctor
M dtor
std::exception

If the M m_ member is to be dynamically allocated, favour a unique_ptr or a shared_ptr over a naked pointer, and allow the smart pointers to manage the object for you; as follows;

#include <iostream>
#include <memory>
using namespace std;
struct M {
    M() { cout << "M ctor" << endl; }
    ~M() { cout << "M dtor" << endl; }
};
struct C {
    unique_ptr<M> m_;
    C() : m_(new M()) { cout << "C ctor" << endl; throw exception(); }
    ~C() { cout << "C dtor" << endl; }
};

The output here mirrors the output above.

like image 184
Niall Avatar answered Oct 10 '22 17:10

Niall