I'm woring against a model which consists of a number of different types (Properties, Parent, Child, etc). Each type is associated with a set of functions from a c api. For example:
Type "Properties":
char* getName(PropertiesHandle);
char* getDescription(PropertiesHandle);
Type "Parent"
PropertiesHandle getProperties(ParentHandle);
ChildHanlde getFirstChild(ParentHandle);
Type "Child"
PropertiesHandle getProperties(ChildHandle);
ParentHanlde getParent(ChildHandle);
ChildHandle getNextChild(ChildHandle);
I have in turn created a set of C++ interfaces for this c api library, as follows:
class IProperties
{
public:
virtual std::string getName() = 0;
virtual std::string getDescription() = 0;
};
class IParent
{
public:
virtual std::shared_ptr<IProperties> getProperties() = 0;
virtual std::shared_ptr<IChild> getFirstChild() = 0;
};
class IChild
{
public:
virtual std::shared_ptr<IProperties> getProperties() = 0;
virtual std::shared_ptr<IParent> getParent() = 0;
virtual std::shared_ptr<IChild> getNextChild() = 0;
};
I then implement these interfaces via the classes Properties, Parent and Child.
So a Child instance will take its specific ChildHandle via its constructor and its getParent function will look something like this:
std::shared_ptr<IParent> getParent()
{
// get the parent handle and wrap it in a Parent object
return std::shared_ptr<IParent>(new Parent(_c_api->getParent(_handle)));
}
Is it reasonable for me to return a shared_ptr here in your opinion. I cant use std::unique_ptr since Google Mock requires parameters and return values of mocked methods to be copyable. I'm mocking these interfaces in my tests via Google Mock.
I'm thinking also about how things might get optimized in the future which might present the possibly of circular references. This could be caused if caching is used to avoid multiple calls to the C api (for example, no need for a child to establish its parent more than once) combined with say the Child constructor taking its Parent. This would then require the use of weak_ptrs which would change the interfaces and a lot of my code...
The key question is: what are the semantics of the returned pointer?
if the returned parent/child/properties object has a lifetime independent of the returning (presumably, in some sense, owning) object, it's reasonable to return shared_ptr: this indicates that the caller and callee have equal rights to decide the object's lifetime
std::shared_ptr<IChild> child = parent->getFirstChild();
// now I can keep child around ... if parent is destroyed, one
// orphaned subtree is magically kept around. Is this desirable?
if the returned object has a lifetime dependent on the callee's own lifetime, then:
shared_ptr will wrongly suggest it's meaningful for the caller to extend the returned object's lifetime beyond that of the calleeunique_ptr will wrongly suggest transfer of ownershipSo, if the caller is just getting a working reference to your object's internal state, without either transfer of ownership or extension of object lifetime, it doesn't suggest using any smart pointer.
Consider just returning a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With