2

I read that smart pointers helpful in situation when constructor generate some exceptions.

The problem is that constructor got some resource before exceptions generation but destructor is not called (and resources permanent busy).

But i can't undestand it properly. My code:

#include <memory>
#include <iostream>

class resOwner {
    public:
    resOwner() {
        std::cout << "Map some huge resources\n";
        throw "hi";
    }

    ~resOwner() {
        std::cout << "Free some huge resources\n";
    }
};

class normal : resOwner {
};

int main (){
    try {
        std::shared_ptr<resOwner> k (new resOwner());
    } catch (...) {}
}

Output is Map some huge resources. How to solve this resource leak with smart pointers?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Stepan Loginov
  • 1,667
  • 4
  • 22
  • 49
  • 1
    Do not worry - memory is freed if c-tor throws - without cooperation from smart pointer - see http://stackoverflow.com/questions/1674980/who-deletes-the-memory-allocated-during-a-new-operation-which-has-exception-in – PiotrNycz Jun 13 '16 at 10:53
  • Whatever resource `resOwner` owns must be some sort of smart pointer `unique_ptr` or `shared_ptr` etc. Otherwise you will end up with resource leak as whatever you planned to cleanup in the destructor won't get called. – Arunmu Jun 13 '16 at 10:54
  • 1
    It's worth pointing out that if a single constructor runs to completion (constructor delegation) and the delegating constructor throws, then the object's destructor will be called. – Blazo Jun 13 '16 at 10:59

3 Answers3

4

How to solve this resource leak with smart pointers?

Wrap the resource in a RAII container, and store the container as a member of resOwner. You don't necessarily need smart pointers.

struct resource {
    resource() {
        std::cout << "Map some huge resources\n";
    }
    ~resource() {
        std::cout << "Free some huge resources\n";
    }
};

struct resOwner {
    resource res;
    resOwner() {
        throw "hi";
    }
};

If that huge resource is a dynamically allocated object, then you don't need to implement a separate resource class, because the standard library already has a container for those: std::unique_ptr. If it is a dynamically allocated array, then you can use std::vector.

The way this works: When the constructor body or one of the sub-object constructor throws, all sub objects (members and base objects) that have already been constructed, will be destroyed. This guarantees that ~resource will be called.

eerorika
  • 232,697
  • 12
  • 197
  • 326
3

You should use smart pointers with resources, they will help you to avoid resource leak. Such as:

class resource {
public:
    resource() {
        std::cout << "resource allocated\n";
    }
    ~resource() {
        std::cout << "resource deallocated\n";
    }
};

class resOwner {
    std::shared_ptr<resource> res;
public:
    resOwner() : res(new resource) {
        std::cout << "Map some huge resources\n";
        throw "hi";
        // res will be destroyed even though exception happened
    }
    ~resOwner() {
        // nothing need to do here
    }
};

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

The exception is thrown before the object actually is created. As no object is created, its destructor won't be called.

On the other hand, if an exception is thrown within the contructor, for all those sub-objects of which the constructor completed execution the destructor will be called. Try this for comparison:

class Resource
{
public:
    Resource()
    {
        std::cout << "constructing resource" << std::endl;
    }
    ~Resource()
    {
        std::cout << "destroying resource" << std::endl;
    }
};

class Owner
{
    ::std::unique_ptr <Resource> theResource;
public:
    Owner()
            : theResource(new Resource())
    {
        std::cout << "Map some huge resources\n";
        throw "hi";
    }

    ~Owner()
    {
        std::cout << "Free some huge resources\n";
    }
};
Aconcagua
  • 24,880
  • 4
  • 34
  • 59