When should I use raw pointers over smart pointers?
No, it's not true. If a function needs a pointer and has nothing to do with ownership, then I strongly believe that a regular pointer should be passed for the following reasons:
- No ownership, therefore you don't know what kind of a smart pointer to pass
- If you pass a specific pointer, like
shared_ptr
, then you won't be able to pass, say,scoped_ptr
The rule would be this - if you know that an entity must take a certain kind of ownership of the object, always use smart pointers - the one that gives you the kind of ownership you need. If there is no notion of ownership, never use smart pointers.
Example1:
void PrintObject(shared_ptr<const Object> po) //bad
{
if(po)
po->Print();
else
log_error();
}
void PrintObject(const Object* po) //good
{
if(po)
po->Print();
else
log_error();
}
Example2:
Object* createObject() //bad
{
return new Object;
}
some_smart_ptr<Object> createObject() //good
{
return some_smart_ptr<Object>(new Object);
}
When to use shared_ptr and when to use raw pointers?
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:
- If you return an object whose lifetime is to be managed by the caller, return
std::unique_ptr
. The caller can assign it to astd::shared_ptr
if it wants. - Returning
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 usestd::enable_shared_from_this
. - You very rarely need
std::weak_ptr
, except when you want to make sense of thelock
method. This has some uses, but they are rare. In your example, if the lifetime of theA
object was not deterministic from the caller's point of view, this would have been something to consider. - If you return a reference to an existing object whose lifetime the caller cannot control, then return a bare pointer or a reference. By doing so, you tell the caller that an object exists and that she doesn't have to take care of its lifetime. You should return a reference if you don't make use of the
nullptr
value.
When is safe wrapping legacy raw pointers from factories with smart pointers?
Only when ownership of object pointed by returned value is transferred to user (i.e. you, who write the code calling that factory). An object returned by factory might be actually owned by framework and would be destroyed properly and (almost) never lost. Using smart pointer on those would result in problems, mainly double deletion.
An example of such is Qt framework, where all visual elements destroy their children when they are destroyed and all QObject elements ar "enumerated". A factory there is hidden from user and mostly related to signal-slot system and metaobject data. There are special cases where smart pointers can be used, but Qt provides own flavor of those.
A factory that doesn't own created objects may require some custom steps to delete object directly, in that case you should use proper deleters for smart pointer.
There is a little misunderstanding about statement that in modern C++ "raw pointers are bad". It actually must be worded "raw owning pointers are bad". If you loose value of raw pointer and there is nothing in program that stores its value for purpose of proper deallocation and releasing of resources, it's an owning pointer.
Whether to pass shared pointer or raw pointer to a function
I would suggest the following approach to looking at code something like this:
There are some other options like weak_ptr
, but for this it is probably not worth looking at.
So for your example, we can see that ThirdParty_DoStuff
does not take ownership, so we won't either, so you can choose between a reference and a pointer depending on if the argument is mandatory or not respectively.
Related Topics
C and C++ Programming on Ubuntu 11.10
What Is "Pch.H" and Why Is It Needed to Be Included as the First Header File
Winapi Sleep() Function Call Sleeps for Longer Than Expected
How to Add a Timed Delay to a C++ Program
Structure of a C++ Object in Memory VS a Struct
Addition of Two Chars Produces Int
Std::Vector, Thread-Safety, Multi-Threading
What Is the Meaning of "Operator Bool() Const"
Simple Way to Unzip a .Zip File Using Zlib
Std::String::C_Str() and Temporaries
Variadic Function Template with Pack Expansion Not in Last Parameter
Confusion Between C++ and Opengl Matrix Order (Row-Major VS Column-Major)
Stack-Buffer Based Stl Allocator
Order of Evaluation of Elements in List-Initialization