We have a pretty standard tree API using shared pointers that looks roughly like this (implementations omitted for brevity):
class node;
using node_ptr = std::shared_ptr<node>;
class node : public std::enable_shared_from_this<node> {
std::weak_ptr<node> parent;
std::vector<node_ptr> children;
public:
virtual ~node() = default;
virtual void do_something() = 0;
void add_child(node_ptr new_child);
void remove_child(node_ptr child);
node_ptr get_parent();
const std::vector<node_ptr>& get_children();
};
class derived_node : public node {
derived_node() = default;
public:
virtual void do_something() override;
static node_ptr create(/* args... */);
};
// More derived node types...
This works just fine and prevents nodes being leaked as you'd imagine. However, I've read on various other answers on SO that using std::shared_ptr in a public API like this is considered bad style and should be avoided.
Obviously this ventures into opinion-based territory, so a couple of concrete questions to avoid this question being closed :-)
Are there any well-known pitfalls to using
shared_ptrs in interfaces like this, which we have so far been fortunate enough to avoid?If so, is there a commonly-used (I hesitate to say "idiomatic") alternative formulation which avoids said pitfalls but still allows for simple memory management for users?
Thanks.
One reason this may be considered bad style may be the additional overhead involved in reference counting that is often (never say never) not actually needed in external APIs, since they often fall into one of two categories:
std::unique_ptrwill usually be a better fit, and it has 0 overhead.