Reputation: 170
I have some code that produces widgets like this:
std::unique_ptr<Widget1> Widget1::Create()
std::unique_ptr<Widget2> Widget2::Create()
Now I have another piece of code that needs to use Widget1
and Widget2
. I'd like for it to have a similar interface, but take the widgets as inputs.
std::unique_ptr<Widget3> Widget3::Create(<?> Widget1, <?> Widget2)
Internally, Widget3
should contain a reference, such as
class Widget3
{
public:
std::unique_ptr<Widget3> Create(<?> Widget1, <?> Widget2)
{
_widget1 = Widget1;
_widget2 = Widget2;
}
void doSomething()
{
std::cout << _widget1->hello() << _widget2->hello();
}
private:
<?> _widget1, _widget2
};
Now I've thought about using a std::shared_ptr
for <?>
because this appears to be the most sane. However... I'm confused on how I should pass it in?
Thoughts?
Upvotes: 1
Views: 1616
Reputation: 69902
The trick here is 'separation of concerns'.
The lifetime of an object is a separate concern to its implementation.
shared_ptr
and unique_ptr
control lifetime. Widgetn objects do things.
If you respect that separation of concerns in your code design, your life will be a happy one, your programs will never go wrong and your colleagues will love you:
#include <iostream>
#include <memory>
#include <string>
struct Widget1 {
std::string hello() { return "widget1"; }
};
struct Widget2 {
std::string hello() { return "widget2"; }
};
struct Widget3 {
// Widget3 objects share their components. This is now documented in the interface here...
Widget3(std::shared_ptr<Widget1> widget1, std::shared_ptr<Widget2> widget2)
: _widget1(std::move(widget1))
, _widget2(std::move(widget2))
{
}
void doSomething()
{
std::cout << _widget1->hello() << _widget2->hello();
}
private:
std::shared_ptr<Widget1> _widget1;
std::shared_ptr<Widget2> _widget2;
};
using namespace std;
auto main() -> int
{
// make a unique Widget3
auto w1a = make_unique<Widget1>();
auto w2a = make_unique<Widget2>();
// note the automatic move-conversion from unique_ptr to shared_ptr
auto w3a = make_unique<Widget3>(move(w1a), move(w2a));
// make unique widget3 that uses shared components
auto w1b = make_shared<Widget1>();
auto w2b = make_shared<Widget2>();
auto w3b = make_unique<Widget3>(w1b, w2b);
// make shared widget3 that shares the same shared components as w3b
auto w3c = make_shared<Widget3>(w1b, w2b);
return 0;
}
The use of static ::create functions is un-necessary. It enforces a memory model on the creator of your object.
If you want to enforce a memory model (such as always creating a shared pointer), do it privately, using the shared-handle-pimpl idiom:
// Widget4 objects have shared-handle semantics.
struct Widget4
{
private:
struct impl {
std::string hello() const { return "hello4"; }
};
public:
Widget4()
: _impl { std::make_shared<impl>() }
{}
std::string hello() const {
return _impl->hello();
}
private:
std::shared_ptr<impl> _impl;
};
Upvotes: 3
Reputation: 15824
If Widget1 and widget2 are going to exist only as member of widget3 and you don't need to refer those variables later independently you can pass them as value to Widget3::create()
std::unique_ptr<Widget1> Widget1::Create()// returns a unique_ptr to widget1
std::unique_ptr<Widget2> Widget2::Create()// returns a unique_ptr to widget2
std::unique_ptr<Widget3> Create(std::unique_ptr<Widget1> Widget1,
std::unique_ptr<Widget2> Widget2)
Otherwise if you prefer to keep objects of Widget1 and Widget2 as shared object with Widget3, use shared_ptr for widget1 and widget2
Upvotes: 0