问题
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.
回答1:
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 ^^;;;; )
回答2:
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.
回答3:
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.,
回答4:
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;
回答5:
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
.
回答6:
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:
- hand out raw pointers (plus either a promise that the pointers can be
delete
d or a Destroy() function to call to deallocate the objects) - accept a function or STL allocator-like argument so you can hook into whatever the user's using for memory management (feel free to default to
new
andstd::allocator
) - have your library users hand you allocated memory buffers (like std::vectors)
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());
来源:https://stackoverflow.com/questions/4389650/confusion-concerning-boostshared-ptr