steazzalini steazzalini - 1 year ago 91
C++ Question

std::unique_ptr and std::shared_ptr as parameters for virtual functions

I'm designing a C++ class interface based on

virtual
methods to be able to provide extensibility points.

Lots of these public methods require
heap
allocated object as parameters.

Since I'm making use of modern C++ patterns I'm planning to use
std::unique_ptr
or
std::shared_ptr
for that but I have doubts on both of them.

Using std::unique_ptr it looks like something like this:

class IFoo {
virtual void doSomethingWithUser(std::unique_ptr<User> user) = 0;
}


Forcing the caller to provide
std::unique_ptr
has downsides:


  • the caller cannot do any operation on the provided user since it has to be moved

  • in case any of
    doSomethingWithUser
    implementation needs to store the user in some container, it is not constructible from
    std::shared_ptr



Using
std::shared_ptr
for all the public methods could solve the problem but we have to pay for extra memory space plus the atomic increment and decrement of the references count.

Is there any rule of thumb I can follow?

Answer Source

If doSomethingWithUser don't need ownership, you shouldn't transfer it to this method.

Indeed, copying a shared pointer or moving a unique pointer effectively transfers the ownership of the resource.

Your functions parameters should reflect your intentions about the assumed ownership of the passed resources.

If you only need to observe the value, and maybe mutating it, you should pass a non-owning handle to your function.

If you need to keep the resource alive and maybe deleting it, then you should pass a owning handle, whether it's a shared it unique ownership.

In your case, the name of the function tells me that you need to "do something with the user", without containing it passed the caller's lifetime. So you should pass a non owning handle. That handle can be a User* or User&, and even a std::reference_wrapper<User> or a boost::optional<User&>, depending on your needs.

Your interface should indeed express what an object should be able to do, and indeed enforce what parameters should each function take, but your interface should also express what ownership it has over it's parameters.

I usually prefer references since they ensure it cannot be null and work well with object in automatic storage. Some may argue that they prefer raw pointers as a non-null non-owning handle, but I strongly disagree with that, because they force the &object syntax and allows null.

There is nothing wrong with non-owning raw pointers. However, raw owning pointers should not be used in modern C++.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download