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?
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.
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.
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.
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.
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.
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
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.
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:
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); }
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
0
for the lifetime of the objectstd::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 alternativewhere they do not compete with references
boost::optional<T&>
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; };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With