1
votes

After reading quite a bit about (smart)-pointers in the context of class members, I am still not sure how to handle to follwoing situation.

I want to create objects of type Foo, either by calling the deafult constructor which instantiates a specifc Bar object, or by passing a some sort of pointer to the one argument constructor of Foo.

So in the snippet below (which does not compile of course), what type should m_bar be?

struct Bar {
    Bar(int k) {} 
    Bar operator=(const Bar&) =delete;
    Bar (const Bar&) = delete;
};


struct Foo {

  Foo() : m_bar(5) {}
  Foo(Bar b) : m_bar(b); 

  Bar m_bar;
};
1
Who owns Bar? main? Does Foo own Bar? Or do you want Foo to have it for as long as it exists, but you also want another function to have Bar for as long as required (shared ownership)?wally
"Who owns Bar? main?" Yes " Does Foo own Bar?" nouser695652
In that case then, perhaps make m_bar a reference?wally
@ flatmouse that is a good idea, but then how would my default constructor look like? I would have to construct m_bar on the heap right because if I its on the stack, the memory is invalid after the constructor is popped form the caller stackuser695652
Either your question makes no sense or you need to edit the code you're presenting: m_bar is an instance member variable. It's concrete. Anything you pass to an instance of Foo will copy into it's own memory. Do you want your code to be "SomeType<Bar> m_bar; please advise me which", or do you want your code to be #ifdef PRODUCTION Bar m_bar; #else SomeType<Bar> m_bar;? In which case - well done - you're not actually unit testing your production code, WTG.kfsone

1 Answers

1
votes
  • I can't use unique_ptr because I still need to access the Bar object outside of Foo.

unique_ptr doesn't prevent you from accessing the object outside of Foo.

If you instead meant "I still need to access the Bar object outside of Foo objects lifetime", then your reasoning is sound. In this situation Foo can not be the sole owner of the object.

  • If I used raw pointers, then I would need to call delete if the deafult constructor of Foo was called but not if the one-arg ctr was called.

A pointer must and may only be deleted by its owner. By stating that Foo must delete the pointer, you imply that Foo owns the pointer.

If the object can also be owned by something else, then the design seems to lead to shared ownership.

  • Then there are shared_ptr, but quoting Herb Sutter "Don’t pass a smart pointer as a function parameter unless you want to use or manipulate the smart pointer itself, such as to share or transfer ownership."

Given the description, it seems that share ownership is exactly what you're trying to do and that is explicitly listed by Herb as one of the situations where passing a smart pointer is appropriate.


Now, since the ownership is conditionally either "here" or "there", shared pointer isn't exactly necessary. It's just the simplest solution, and therefore a good solution.

You could imagine a maybe_unique_ptr for your situation. Such smart pointer does not exist in the standard library. Following alias template might work for you but depends on boost:

template<class T>
using maybe_unique_ptr = boost::variant<std::unique_ptr<T>, T*>;

struct Foo {
    Foo()       : bar(std::make_unique<Bar>(5)) {}
    Foo(Bar* b) : bar(b) {}

    maybe_unique_ptr<Bar> bar;
};

This of course means that you must access the instance in the variant using boost::apply_visitor, which makes it more cumbersome.