Reputation: 25353
My question revolves around whether or not I must expose my use of the boost::shared_ptr from my interface and whether or not I should expose raw pointers or references from my interface.
Consider the case of a Person who has an Employeer. Employeer internally maintains all of its employees in a vector< shared_ptr< Person > >
. Because of this, do best practices dictate that any interface involving Person should be a shared_ptr wrapped person?
For example, are all or only some of these ok:
Person Employeer::getPresidentCopy();
Person& Employeer::getPresidentRef();
Person* Employeer::getPresidentRawPtr();
shared_ptr<Person> Employeer::getPresidentSharedPtr();
Or for example:
void Employeer::hireByCopy(Person p);
void Employeer::hireByRef(Person& p);
void Employeer::hireByRawPtr(Person* p);
void Employeer::hireBySharedPtr(shared_ptr<Person> p);
If I later want to change the implementation to use johns_very_own_shared_ptr instead of the boost variety, am I trapped in the old implementation?
On the other hand, if I expose raw pointers or references from the interface, do I risk someone deleting the memory out from under the shared_ptr? Or do I risk the shared_ptr being deleted and making my reference invalid?
See my new question for an example involving this.
Upvotes: 0
Views: 295
Reputation: 20021
I work on a moderately sized project that links in several libraries. Some of those libraries have their own memory management subsystems (APR, MFC) and really annoy me. Whether their view of the world is good or bad, it's entirely different from everybody else's and requires a little more code than they otherwise would.
Additionally, those libraries make swapping out malloc
or new
with jemalloc or the Boehm-Demers-Weiser garbage collector much harder (on Windows it's already hard enough).
I use shared pointers a lot in my own code, but I prefer not to tell others how to manage their memory. Instead, hand out objects whenever possible (letting the library user decide when and how to allocate the memory), and when it's not possible do one of:
delete
d or a Destroy()
function to call to deallocate the objects)new
and std::allocator
)std::vector
s)Using this kind of library doesn't have to be nasty:
// situation (1) from above
std::shared_ptr<Foo> foo(Library_factory::get_Foo(), Library_factory::deallocate);
// situation (2) above (declaration on next line is in a header file)
template<typename allocator=std::allocator<Foo> > Foo* library_function_call();
boost::shared_ptr<Foo> foo = library_function_call();
// situation (3) above, need to fill a buffer of ten objects
std::vector<Foo> foo_buffer(10);
fill_buffer(&foo_buffer[0], foo_buffer.size());
Upvotes: 0
Reputation: 61479
For example, are all or only some of these ok:
It depends on what you're trying to accomplish. Why does the vector hold shared_ptr
s instead of just directly storing Person
s by value? (And have you considered boost::ptr_vector
?)
You should also consider that maybe what you really ought to hand out is a weak_ptr
.
If I later want to change the implementation to use johns_very_own_shared_ptr instead of the boost variety, am I trapped in the old implementation?
Pretty much, but it's not impossible to fix. (I suspect that in C++0x, liberal use of the auto
keyword will make this easier to deal with, since you won't have to modify the calling code as much, even if it didn't use typedef
s.) But then, why would you ever want to do that?
On the other hand, if I expose raw pointers or references from the interface, do I risk someone deleting the memory out from under the shared_ptr?
Yes, but that's not your problem. People can extract a raw pointer from a shared_ptr
and delete
it, too. But if you want to avoid making things needlessly unsafe, don't return raw pointers here. References are much better because nobody ever figures they're supposed to delete &reference_received_from_api;
. (I hope so, anyway ^^;;;; )
Upvotes: 3
Reputation: 54128
The only reason to hand out a shared_ptr
here is if the lifetime of the returned object reference is not tied directly to the lifetime of its residence in the vector.
If you want somebody to be able to access the Person
after they stop being an Employee
, then shared_ptr
would be appropriate. Say if you are moving the Person
to the vector
for a different Employer
.
Upvotes: 1
Reputation: 10260
I would introduce a typedef
and insulate myself against changes. Something like this:
typedef std::shared_ptr<Person> PersonPtr;
PersonPtr Employeer::getPresident() const;
I place typedefs like this one in a (just one) header together with forward declarations. This makes it easy to change if I would ever want to.
Upvotes: 3
Reputation: 2713
I shouldn't give user raw pointers, when you use shared_ptr
s. User could delete it, what will cause double deletion.
To hide usage of boost:shared_ptr
you can use typedef
to hide actual type, and use this new type instead.
typedef boost::shared_ptr<Person> Person_sptr;
Upvotes: 2
Reputation: 145204
You don't have to hand out shared_ptr, but if you hand out raw pointers you run the risk of some raw pointer persisting after the object has been destroyed.
With fatal consequences.
So, handing out references generally OK (if client code takes address then that's no more your fault than if client code takes address of *shared_ptr), but raw pointers, think first.
Cheers & hth.,
Upvotes: 2