I know that the go-to rule is to always use shared_ptr
s or other smart pointers. However, I'm not sure how exactly to implement it without copy ctor'ing with std::make_shared
. I'll elaborate:
The problem
I want to create a tree-like expression, like:
struct foo{
std::vector<foo*> v;
};
This allows me to do the following:
foo add(foo& f1, foo& f2){
return foo {&f1, &f2};
}
That's good, because then the two will become children of a new node.
I want the following to be evaluated correctly:
foo x, y;
auto z = add(x,y);
assert(z.v[0] == &x && z.v[1] == &y);
This way, there's no copying and it's all good. However, we have a resource management issue here.
Addressing the resource management issue
If these elements are allocated on the heap, and someone unknowingly might, then we'll run into a memory leak. So the best way to do this is to use smart pointers for RAII:
struct foo{
std::vector<std::shared_ptr<foo> > v;
};
foo add(foo& f1, foo& f2){
return foo {std::make_shared<foo>(f1), std::make_shared<foo>(f2)};
}
This is good, but we're calling the copy ctor of f1 and f2 here. Suppose copying takes a long time and we don't want to incur that cost.
We definitely, cannot do:
foo add(foo& f1, foo& f2){
return foo {std::shared_ptr<foo>(&f1), std::shared_ptr<foo>(&f2)};
}
because whenever we remove z
in z = x+y
, we will call delete
on each of x
and y
, which could be allocated on the stack. And we definitely do not want to delete
things on the stack.
So what should I do in this case?
Let me know if I should supply more information on the context.
Accept by smart pointer. Applying the notion of smart pointers correctly means you have to think about the ownership semantics of your data, and not about calling new/delete automatically.
You accept a reference to a non-const object. This does not imply sharing, only modification of the objects. And as you correctly noted, just creating smart pointers to them spells disaster.
The caller of
add
(even if it's just you) must know the data they pass is about to be shared. And the only way for them to know is if they pass the smart pointer themselves. So you should changeadd
into this:Now, the only way a user of
add
can blow this up is intentionally. But they most likely won't. Furthermore, now said user has much more flexibility in calling your code. They can control the allocator and deleter for each object they pass.