Question

I'm well aware of the RAII pattern and std::unique_ptr and other "smart pointers" in C++11, but there's still some cases which I can't figure out how to handle nicely without having a goto Cleanup section which does the cleanup finally.

Specifically, I'm thinking about Windows programming where sometimes i'll want to leak handles and other times I won't.

If I have a function that looks kind of like he following:

PROCESS_INFORMATION p;
if (!CreateProcess(... &p))
{
  throw windows_error(GetLastError());
}

DWORD r = WaitForSingleObject(h, 5000);
if (r == WAIT_TIMEOUT || r == WAIT_FAILED)
{
  // terminate process, close handles
  throw ...;
}

if (!SetEnvironmentVariable(...))
{
  // terminate process, close handles
  throw windows_error;
}

(a few other operations that if they fail i have cleanup to do).

return process handle;

I don't really see how unique_ptr can help me here, unless i use release() on the unique_ptr after all the if to indicate success/tell the unique_ptr to not clean it up. (I can create a special deleter for unique_ptr so it cleans up windows handles properly). However, my question is that is such a usage of release() on smart pointers "correct" for cases where i want to leak allocated memory/handles back to the caller? Is there a better way to do it? Returning a shared_ptr can work too, I suppose...

Was it helpful?

Solution

When you want to pass a resource from your function, then there are two cases: either the resource is copyable (it has accessible copy constructor) or not.

If the resource is copyable (e.g. shared_ptr), then you can simply copy it to wherever it needs to go and you're done. When your function returns, it's only your copy of the resource that's destroyed.

If the resource is not copyable (e.g. unique_ptr), then you need to move it, which is what the move semantics in C++ 11 is all about.

When you move the resource to a new location, the old location becomes empty, so when its destructor is called, there is nothing to do.

If you're passing the resource using return, then you don't need to do anything special, return moves automatically if it can.

For example:

std::unique_ptr<resource> get_resource()
{
    std::unique_ptr<resource> result(new resource());

    if (result->is_something_wrong())
    {
        throw std::exception();
    }

    return result;
}

If you want to pass the resource to a field, or something like that, then you need to explicitly say that you want to move it by using std::move:

class resource_user
{
    void init_resource()
    {
        std::unique_ptr<resource> result(new resource());

        if (result->is_something_wrong())
        {
            throw std::exception();
        }

        resource_ = std::move(result);
    }

    std::unique_ptr<resource> resource_;
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top