Hello, OnlineGDB Q&A section lets you put your programming query to fellow community users. Asking a solution for whole assignment is strictly not allowed. You may ask for help where you are stuck. Try to add as much information as possible so that fellow users can know about your problem statement easily.

Why doesn't the destructor on a smart pointer get called when a program is terminated on exception?

0 votes
asked Dec 20, 2021 by Michael Rosinsky (120 points)

One of the main benefits of using smart pointers over raw pointers is that they are exception safe, meaning that if a program terminates before reaching the end of scope, the underlying object will still be destroyed, preventing a memory leak.

In this program, the constructor and destructor are both printed, as expected:

struct Entity {
    
    Entity() { std::cout << "Created Entity" << std::endl; }
    
    ~Entity() { std::cout << "Destroyed Entity" << std::endl; }
    
};

int main() {
    
    std::shared_ptr<Entity> p = std::make_shared<Entity>();
    
}

However, I would expect the same behavior here, but "Destroyed Entity" is not displayed after the exception is reached. 

int main() {
    
    std::shared_ptr<Entity> p = std::make_shared<Entity>();
    
    throw;
    
}

Is it still destroying the underlying object and just not displaying it? Or am I misunderstanding exception safety on a conceptual level? Thanks

1 Answer

0 votes
answered Dec 27, 2021 by Peter Minarik (86,040 points)

When an exception is thrown and it is not handled by the running application, the exception reaches the operating system and the operating system kills the application and frees up (forcefully) all the resources obtained by the application. This does not involve calling any constructors though. But it ensures no resources are hogged by terminated programs, so you won't have memory leaks.

If you want to observe exception-safe smart pointers, you have to catch the exception and handle it yourself (i.e. prevent the exception to reach the OS and your application to be killed).

Please, consider the below example:

#include <iostream>
#include <stdexcept>
#include <memory>

class Foo
{
private:
    int _id;
    
public:
    Foo(int id) : _id(id) { std::cout << "Foo(" << _id << ")" << std::endl; }
    ~Foo() { std::cout << "~Foo(" << _id << ")" << std::endl; }
};

int main()
{
    try
    {
        std::shared_ptr<Foo> f = std::make_shared<Foo>(1); // Destructor will be called when exception is thrown in the constructor.
        Foo * f2 = new Foo(2); // Destructor won't be called when exception is thrown in the constructor.
        throw std::runtime_error("ERROR!!!");
    }
    catch (const std::runtime_error & ex)
    {
        std::cout << "Exception caught: " << ex.what() << std::endl;
    }
    return 0;
}

I hope this helps.

Welcome to OnlineGDB Q&A, where you can ask questions related to programming and OnlineGDB IDE and and receive answers from other members of the community.
...