class B;
class A
{
public:
A ()
: m_b(new B())
{
}
shared_ptr GimmeB ()
{
return m_b;
}
private:
shared_ptr&l
I agree with your opinion that shared_ptr
is best used when explicit sharing of resources occurs, however there are other types of smart pointers available.
In your precise case: why not return a reference ?
A pointer suggests that the data might be null, however here there will always be a B
in your A
, thus it will never be null. The reference asserts this behavior.
That being said, I have seen people advocating the use of shared_ptr
even in non-shared environments, and giving weak_ptr
handles, with the idea of "securing" the application and avoiding stale pointers. Unfortunately, since you can recover a shared_ptr
from the weak_ptr
(and it is the only way to actually manipulate the data), this is still shared ownership even if it was not meant to be.
Note: there is a subtle bug with shared_ptr
, a copy of A
will share the same B
as the original by default, unless you explicitly write a copy constructor and a copy assignment operator. And of course you would not use a raw pointer in A
to hold a B
, would you :) ?
Of course, another question is whether you actually need to do so. One of the tenets of good design is encapsulation. To achieve encapsulation:
You shall not return handles to your internals (see Law of Demeter).
so perhaps the real answer to your question is that instead of giving away a reference or pointer to B
, it should only be modified through A
's interface.
I found that the C++ Core Guidelines give some very useful hints for this question:
To use raw pointer(T*) or smarter pointer depends on who owns the object (whose responsibility to release memory of the obj).
own :
smart pointer, owner<T*>
not own:
T*, T&, span<>
owner<>, span<> is defined in Microsoft GSL library
here is the rules of thumb:
1) never use raw pointer(or not own types) to pass ownership
2) smart pointer should only be used when ownership semantics are intended
3) T* or owner designate a individual object(only)
4) use vector/array/span for array
5) To my undetstanding, shared_ptr is usually used when you don't know who will release the obj, for example, one obj is used by multi-thread
Your analysis is quite correct, I think. In this situation, I also would return a bare B*
, or even a [const] B&
if the object is guaranteed to never be null.
Having had some time to peruse smart pointers, I arrived at some guidelines which tell me what to do in many cases:
std::unique_ptr
. The caller can assign it to a std::shared_ptr
if it wants.std::shared_ptr
is actually quite rare, and when it makes sense, it is generally obvious: you indicate to the caller that it will prolong the lifetime of the pointed-to object beyond the lifetime of the object which was originally maintaining the resource. Returning shared pointers from factories is no exception: you must do this eg. when you use std::enable_shared_from_this
.std::weak_ptr
, except when you want to make sense of the lock
method. This has some uses, but they are rare. In your example, if the lifetime of the A
object was not deterministic from the caller's point of view, this would have been something to consider.nullptr
value.