Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I use raw pointers over smart pointers?

After reading this answer, it looks like it is a best practice to use smart pointers as much as possible, and to reduce the usage of "normal"/raw pointers to minimum.

Is that true?

like image 687
Alon Gubkin Avatar asked Jul 13 '11 07:07

Alon Gubkin


People also ask

In what kind of circumstances would you use a raw pointer instead of a smart pointer?

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.

When should you stop using smart pointers?

Since smart pointers are indicators about data ownership, I tend to avoid them when I don't own the data. Such a case occurs when referencing objects with ownership over the current scope (e.g. “parent” pointers). If a parent object owns the current scope and has a guaranteed existence, use a reference.

When should shared_ptr be used?

So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object. When to use shared_ptr? Use shared_ptr if you want to share ownership of a resource.

What is the difference between raw pointer and smart pointer?

OLD ANSWER. A smart pointer is a class that wraps a 'raw' (or 'bare') C++ pointer, to manage the lifetime of the object being pointed to. There is no single smart pointer type, but all of them try to abstract a raw pointer in a practical way. Smart pointers should be preferred over raw pointers.

Why do we need smart pointers in C++?

Most of these problems arise from the number of details we have to keep in mind in order to write bug-free code. Luckily, C++ provides three smart pointer types that eliminate many of the raw headaches: If you’re unfamiliar with these interfaces, please review my introduction to C++ smart pointers before continuing.

What are the disadvantages of using raw pointers?

Using raw pointers leave you susceptible to a variety of bugs: Freeing memory that shouldn’t be freed (e.g. pointer to statically allocated variable) Freeing memory incorrectly – free () vs aligned_free (), delete vs delete [] Thinking memory is still allocated after being freed because the pointer itself was not updated to NULL

When should you avoid using smart pointers?

Since smart pointers are indicators about data ownership, I tend to avoid them when I don’t own the data. Such a case occurs when referencing objects with ownership over the current scope (e.g. “parent” pointers). If a parent object owns the current scope and has a guaranteed existence, use a reference.


2 Answers

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); } 
like image 168
Armen Tsirunyan Avatar answered Sep 21 '22 09:09

Armen Tsirunyan


Using smart pointers to manage ownership is the right thing to do. Conversely, using raw pointers wherever ownership is not an issue is not wrong.

Here are some perfectly legitimate use of raw pointers (remember, it is always assumed they are non-owning):

where they compete with references

  • argument passing; but references can't be null, so are preferable
  • as class members to denote association rather than composition; usually preferable to references because the semantics of assignment are more straightforward and in addition an invariant set up by the constructors can ensure that they are not 0 for the lifetime of the object
  • as a handle to a (possibly polymorphic) object owned somewhere else; references can't be null so again they are preferable
  • std::bind uses a convention where arguments that are passed are copied into the resulting functor; however std::bind(&T::some_member, this, ...) only makes a copy of the pointer whereas std::bind(&T::some_member, *this, ...) copies the object; std::bind(&T::some_member, std::ref(*this), ...) is an alternative

where they do not compete with references

  • as iterators!
  • argument passing of optional parameters; here they compete with boost::optional<T&>
  • as a handle to a (possibly polymorphic) object owned somewhere else, when they can't be declared at the site of initialization; again, competing with boost::optional<T&>

As a reminder, it's almost always wrong to write a function (that is not a constructor, or a function member that e.g. takes ownership) that accepts a smart pointer unless it in turn pass it to a constructor (e.g. it's correct for std::async because semantically it's close to being a call to the std::thread constructor). If it's synchronous, no need for the smart pointer.


To recap, here's a snippet that demonstrates several of the above uses. We're writing and using a class that applies a functor to every element of an std::vector<int> while writing some output.

class apply_and_log { public:     // C++03 exception: it's acceptable to pass by pointer to const     // to avoid apply_and_log(std::cout, std::vector<int>())     // notice that our pointer would be left dangling after call to constructor     // this still adds a requirement on the caller that v != 0 or that we throw on 0     apply_and_log(std::ostream& os, std::vector<int> const* v)         : log(&os)         , data(v)     {}      // C++0x alternative     // also usable for C++03 with requirement on v     apply_and_log(std::ostream& os, std::vector<int> const& v)         : log(&os)         , data(&v)     {}     // now apply_and_log(std::cout, std::vector<int> {}) is invalid in C++0x     // && is also acceptable instead of const&&     apply_and_log(std::ostream& os, std::vector<int> const&&) = delete;      // Notice that without effort copy (also move), assignment and destruction     // are correct.     // Class invariants: member pointers are never 0.     // Requirements on construction: the passed stream and vector must outlive *this      typedef std::function<void(std::vector<int> const&)> callback_type;      // optional callback     // alternative: boost::optional<callback_type&>     void     do_work(callback_type* callback)     {         // for convenience         auto& v = *data;          // using raw pointers as iterators         int* begin = &v[0];         int* end = begin + v.size();         // ...          if(callback) {             callback(v);         }     }  private:     // association: we use a pointer     // notice that the type is polymorphic and non-copyable,     // so composition is not a reasonable option     std::ostream* log;      // association: we use a pointer to const     // contrived example for the constructors     std::vector<int> const* data; }; 
like image 37
Luc Danton Avatar answered Sep 19 '22 09:09

Luc Danton