When Should I Use Raw Pointers Over Smart Pointers

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 a std::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 use std::enable_shared_from_this.
  • You very rarely need 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.
  • 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:

Sample Image

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



Leave a reply



Submit