88

I find smart pointers to be a lot more comfortable than raw pointers. So is it a good idea to always use smart pointers? ( Please note that I am from Java background and hence don't much like the idea of explicit memory management. So unless there are some serious performance issues with smart pointers, I'd like to stick with them. )

Note: Though I come from Java background, I understand the implementation of smart pointers and the concepts of RAII quite well. So you can take this knowledge for granted from my side when posting an answer. I use static allocation almost everywhere and use pointers only when necessary. My question is merely: Can I always use smart pointers in place of raw pointers???

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
Dony Borris
  • 873
  • 1
  • 7
  • 9
  • 10
    using the word "always" is never a good thing when talking about good practices since there are circumstances when the usage of a pattern or guideline will not be useful for numerous reasons. M. – Max Mar 16 '10 at 12:49
  • @Neil Yeah I meant that only. – Dony Borris Mar 16 '10 at 12:58
  • I mean this with no offense, but it's clear you need to get a good book and start from the beginning. Your terminology is wrong, and I'm afraid your code is very "non-C++". – GManNickG Mar 17 '10 at 01:32
  • 2
    damn, nothing but salty c++ programmers here – Bruno Oct 08 '18 at 12:51

10 Answers10

92

Given the several edits, I have the impression that a comprehensive summary would be useful.

1. When not to

There are two situations where you should not use smart pointers.

The first is the exact same situation in which you should not use a C++ class in fact. IE: DLL boundary if you do not offer the source code to the client. Let say anecdotal.

The second happens much more often: smart manager means ownership. You may use pointers to point at existing resources without managing their lifetime, for example:

void notowner(const std::string& name)
{
  Class* pointer(0);
  if (name == "cat")
    pointer = getCat();
  else if (name == "dog")
    pointer = getDog();

  if (pointer) doSomething(*pointer);
}

This example is constrained. But a pointer is semantically different from a reference in that it may point to an invalid location (the null pointer). In this case, it's perfectly fine not to use a smart pointer in its stead, because you don't want to manage the lifetime of the object.

2. Smart managers

Unless you are writing a smart manager class, if you use the keyword delete you are doing something wrong.

It is a controversial point of view, but after having reviewed so many example of flawed code, I don't take chances any longer. So, if you write new you need a smart manager for the newly allocated memory. And you need it right now.

It does not mean you are less of a programmer! On the contrary, reusing code that has been proved to work instead of reinventing the wheel over and over is a key skill.

Now, the real difficulty start: which smart manager ?

3. Smart pointers

There are various smart pointers out of there, with various characteristics.

Skipping std::auto_ptr which you should generally avoid (its copy semantic is screwed).

  • scoped_ptr: no overhead, cannot be copied or moved.
  • unique_ptr: no overhead, cannot be copied, can be moved.
  • shared_ptr / weak_ptr: some overhead (reference counting), can be copied.

Usually, try to use either scoped_ptr or unique_ptr. If you need several owners try to change the design. If you can't change the design and really need several owners, use a shared_ptr, but beware of references cycles that ought to be broken using a weak_ptr somewhere in the midst.

4. Smart containers

Many smart pointers are not meant to be copied, therefore their use with the STL containers are somewhat compromised.

Instead of resorting to shared_ptr and its overhead, use smart containers from the Boost Pointer Container. They emulate the interface of classic STL containers but store pointers they own.

5. Rolling your own

There are situations when you may wish to roll your own smart manager. Do check that you did not just missed some feature in the libraries your are using beforehand.

Writing a smart manager in the presence of exceptions is quite difficult. You usually cannot assume that memory is available (new may fail) or that Copy Constructors have the no throw guarantee.

It may be acceptable, somewhat, to ignore the std::bad_alloc exception and impose that Copy Constructors of a number of helpers do not fail... after all, that's what boost::shared_ptr does for its deleter D template parameter.

But I would not recommend it, especially for a beginner. It's a tricky issue, and you're not likely to notice the bugs right now.

6. Examples

// For the sake of short code, avoid in real code ;)
using namespace boost;

// Example classes
//   Yes, clone returns a raw pointer...
// it puts the burden on the caller as for how to wrap it
//   It is to obey the `Cloneable` concept as described in 
// the Boost Pointer Container library linked above
struct Cloneable
{
  virtual ~Cloneable() {}
  virtual Cloneable* clone() const = 0;
};

struct Derived: Cloneable
{
  virtual Derived* clone() const { new Derived(*this); }
};

void scoped()
{
  scoped_ptr<Cloneable> c(new Derived);
} // memory freed here

// illustration of the moved semantics
unique_ptr<Cloneable> unique()
{
  return unique_ptr<Cloneable>(new Derived);
}

void shared()
{
  shared_ptr<Cloneable> n1(new Derived);
  weak_ptr<Cloneable> w = n1;

  {
    shared_ptr<Cloneable> n2 = n1;          // copy

    n1.reset();

    assert(n1.get() == 0);
    assert(n2.get() != 0);
    assert(!w.expired() && w.get() != 0);
  } // n2 goes out of scope, the memory is released

  assert(w.expired()); // no object any longer
}

void container()
{
  ptr_vector<Cloneable> vec;
  vec.push_back(new Derived);
  vec.push_back(new Derived);

  vec.push_back(
    vec.front().clone()         // Interesting semantic, it is dereferenced!
  );
} // when vec goes out of scope, it clears up everything ;)
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 4
    Great answer! :) I guess Mr Butterworth could learn something from this. – Dony Borris Mar 16 '10 at 17:04
  • 2
    I personally like Neil's answers (in general, and this one in particular), I just thought the subject required a more in-depth explanation given how memory management is tricky and how "relatively" new the libraries are (I am thinking Pointer Container here, dated 2007). – Matthieu M. Mar 16 '10 at 17:10
  • What do you mean with "Smart Managers"? That section of the answer makes no sense to me. Also, the semantic of `std::auto_ptr` is different from what most people expect, but it makes sense and leads to avoid design problems in code, saying "generally avoid it" is non-sense. – Frunsi Mar 17 '10 at 01:37
  • 1
    @frunsi: if saying "generally avoid it" is nonsense, you need to get on to the ISO standard committee quick and explain to them why. They're about to make a terrible mistake by deprecating `auto_ptr` in C++0x, recommending the use of `unique_ptr` instead ;-). To be fair, `unique_ptr` relies on move construction/assignment to be a replacement for `auto_ptr`, if you want strict transferable ownership in C++03 you don't have a lot of choice. – Steve Jessop Mar 17 '10 at 02:13
  • @Steve: Alright! Looks like unique_ptr is the way to go when move semantics are available. Until then auto_ptr is still useful but will be removed in 100 years or so ;) If we all start writing C++0x code next year (I hope so) we should avoid auto_ptr now, but I suspect it, so .. :) Just kidding, you're right it should be avoided now. – Frunsi Mar 17 '10 at 02:42
  • @frunsi: Smart Managers is a larger concept that Smart Pointers as it includes the concept of Boost Pointer Containers as well (which are not implemented in term of smart pointer). It's a term I coined myself, to refer (at large) to objects that manage the memory. Which part of the section you don't understand ? I'd be happy to review it for if it's unclear to you it may very well be unclear to many others. – Matthieu M. Mar 17 '10 at 07:30
  • @Matthieu: Thx, can you give me an example, just a keyword, class name or link? Sounds interesting. – Frunsi Mar 17 '10 at 18:15
18

Smart pointers do perform explicit memory management, and if you don't understand how they are doing it, you are in for a world of trouble when programming with C++. And remember that memory isn't the only resource that they manage.

But to answer your question you should prefer smart-pointers as a first approximation to a solution, but possibly be prepared to ditch them when necessary. You should never use pointers (or any sort) or dynamic allocation when it can be avoided. For example:

string * s1 = new string( "foo" );      // bad
string s2( "bar" );    // good

Edit: To answer your suplementary question "Can I always use smart pointers in place of raw pointers??? Then, no you can't. If (for example) you need to implement your own version of operator new, you would have to make it return a pointer, not a smart pointer.

  • 12
    Utterly unhelpful answer. I wish I had enough rep to downvote this. – Dony Borris Mar 16 '10 at 12:32
  • 2
    @Dony: really? What's unhelpful? That you may have the wrong conception of how smart pointers work? Here: I've upvoted it for you! – Pontus Gagge Mar 16 '10 at 12:35
  • 1
    @Neil Yes, the other answers reflect that. – Dony Borris Mar 16 '10 at 12:36
  • 12
    @Dony I generally downvote wrong, rather than simply unhelpful answers. After all, it can be difficult to know exactly what the questioner needs to learn to become enlightened. – Philip Potter Mar 16 '10 at 12:40
  • @Neil: +1. IMHO this is a simplest possible but accurate answer to the question. As with some areas of C++ like the smart-pointers, the users need to know more than just the basics. It is best if they are redirected to read more about such aspects. In a way it may be a little difficult for C++ beginners to decipher the entire gist of your answer. – Abhay Mar 16 '10 at 12:58
  • 4
    Unfortunately, Neil's answers often come served with a side of condescension. He should stop answering questions that frustrate him because the world isn't as smart or as experienced as he is. –  Mar 16 '10 at 13:24
  • @STingRaySC I won't bother asking where the condescension is. And your own answering style is not without its more abrasive side. –  Mar 16 '10 at 13:26
  • 3
    @Neil: you took several jabs at the OP's competence level in your comments. Don't get me wrong though. I'm all for it. I think anyone who hasn't read the language standard front to back at least 5 times is in a "world of trouble when programming with C++." –  Mar 16 '10 at 13:30
  • 1
    @STingRaySC The only "jab" I can possibly see is in the first sentence, in which I meant the "you"'s to be generic. Anyway, the OP didn't complain about condescension or "jabs" but about the usefulness of the answer. –  Mar 16 '10 at 13:34
  • @STingRaySC: '... anyone who hasn´t read the language standard ' [STL and boost implementations] ' at least 5 times' [or intends on having a social life] ' is in a "world of trouble when programming with C++' -- in case anyone feels offended: it is only a joke, you do not need to read the complete boost implementation, just forget about the social life :P – David Rodríguez - dribeas Mar 16 '10 at 13:41
  • +1: "... if you don't understand how they are doing it, you are in for a world of trouble ..." this is condescending? This is the truth, ya boo to anyone that takes offense at that. P.S. I've been taken to task recently for leaving "sarcastic" comments on a noobs question. As my comments weren't intended to be hurtful (and to be honest I don't think they were) I apologised, however my initial reaction was "You think that's sarcastic? *Give me a feckin minute and I'll show you sarcasm sonny boy*!" – Binary Worrier Mar 16 '10 at 13:43
  • Cripes guys, all I'm saying is that if you think the question is poor, or the OP's competency is such that they would need tremendous study even to understand the question they are asking, then stay away from the question. Neil, I'm sorry. I didn't aim to offend you. My answers have been condescending at times too, but I'm trying to steer clear of those questions now. You can't help everyone. If the quality of the question is poor (for whatever reason), just don't answer it. People will learn to ask better questions when they don't get answers. –  Mar 16 '10 at 14:00
  • 3
    I see the Stereotype Continuity Brigade is out in full force to keep up the image of geeks not having social skills. – JUST MY correct OPINION Mar 17 '10 at 01:27
  • 2
    -1 for "do this and that" without reasoning, and using a specific auto-ptr implementation as "a first approximation" is the worst thing to do if he does not yet know the semantics and details of that specific implementation. – Frunsi Mar 17 '10 at 01:41
  • @frunsi Nothing about SO says you have to supply reasons, and most answers do not - are you going to downvote all of them too? Also, I never mentioned auto_ptr and specifically said that it was crucial for users to understand the semantics of smart pointers. Next time, please take the trouble to read the answer before downvoting, or is there some little agenda here? –  Mar 17 '10 at 09:15
  • @Neil: I see, I meant smart-pointers, not auto-pointers, and not auto_ptr in particular. I read the answer. The remaining part of my comment still applies. – Frunsi Mar 17 '10 at 16:36
14

Usually you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects. (Since you come from Java you're probably tempted allocate everything with new, which is not recommended.)

This approach ("RAII") saves you from worrying about pointers most of the time.

When you have to use pointers it depends on the situation and why exactly you need pointers, but usually smart pointers can be used. It might not be always (in bold) be the best option, but this depends on the specific situation.

sth
  • 222,467
  • 53
  • 283
  • 367
  • 8
    So, what are those situations, on which "it depends"? Why's noone telling it? – P Shved Mar 16 '10 at 12:50
  • 1
    I can think of a couple of situations: Performance requirements might make shared pointers unsuitable (`boost::scoped_ptr` could still be used then, but perhaps you don't want to take a dependency on boost then?) -- or you need to interface with a C API, in which case raw pointers are more consistent. If you need to iterate over an array, your iterators will probably be raw pointers as well. – jalf Mar 16 '10 at 13:21
  • 1
    If you're creating an object that can outlive the instance creating it, you clearly can't simply embed it within the other object. – Ben Voigt Mar 16 '10 at 13:22
  • @Pavel Shved: I did add one such case: when you need a reference to another object, you need to be able to reset it, and the object cannot be deleted (lifetime is handled somewhere else: it is a member of another object or may be stack allocated) – David Rodríguez - dribeas Mar 16 '10 at 13:29
  • @jalf: I don´t think that `auto_ptr` or `unique_ptr` (depending on the version of the standard you want to use) will have any overhead. – David Rodríguez - dribeas Mar 16 '10 at 13:30
  • 1
    @Ben Voigt: in the general case your example is not valid. You can return an `auto_ptr`, `unique_ptr` or `shared_ptr` just as you would pass a raw pointer out of your scope transferring ownership. `scoped_ptr` is the only smart pointer of the set that will not be able to transfer/share ownership – David Rodríguez - dribeas Mar 16 '10 at 13:33
  • 1
    @Ben: boost shared pointers have a mechanism to address this, see this question I asked: http://stackoverflow.com/questions/1403465/what-is-boosts-shared-ptrshared-ptry-const-r-t-p-used-for . Basically, you can have a shared pointer which holds a reference (int he counting sense) to an object, but when referenced, returns a different pointer (in this case a member of the referenced object). – Evan Teran Mar 16 '10 at 13:40
  • One situation I had in mind was that if you have lots of cyclic dependencies, simple usage of `shared_ptr` won't help. `weak_ptr` can break the cycles, but it might, depending on the situation, just be simpler to explicitly control what is freed when be using raw pointers. – sth Mar 16 '10 at 13:50
  • Don't use auto_ptr in STL containers. – Brian Mar 16 '10 at 13:56
  • @David: I know, that's why I said *shared* pointers might be unsuitable. :) Other types of smart pointers should be fine. – jalf Mar 16 '10 at 17:50
  • @David, Evan: I was responding to what the post said, "you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects". This doesn't work for instances whose lifetime exceeds that of the creating object. Of course a smart pointer is still better than a raw pointer, but "don't use either" doesn't cut it. – Ben Voigt Mar 17 '10 at 00:40
9

A good time not to use smart pointers, is at the interface boundary of a DLL. You don't know whether other executables will be built with the same compiler/libraries. Your system's DLL calling convention won't specify what standard or TR1 classes look like, smart pointers included.

Within an executable or library, if you want to represent ownership of the pointee, then smart pointers are on average the best way to do it. So it's fine to want to always use them in preference to raw. Whether you actually can always use them is another matter.

For a concrete example when not to - suppose you are writing a representation of a generic graph, with vertices represented by objects and edges represented by pointers between the objects. The usual smart pointers will not help you: graphs can be cyclic, and no particular node can be held responsible for the memory management of other nodes, so shared and weak pointers are insufficient. You might for example put everything in a vector and use indices instead of pointers, or put everything in a deque and use raw pointers. You could use shared_ptr if you wanted, but it won't add anything except overhead. Or you could look for mark-sweep GC.

A more marginal case: I prefer to see functions take a parameter by pointer or reference, and promise not to retain a pointer or reference to it, rather than take a shared_ptr and leave you wondering whether maybe they retain a reference after they return, maybe if you modify the referand ever again you'll break something, etc. Not retaining references is something that often isn't documented explicitly, it just goes without saying. Maybe it shouldn't, but it does. Smart pointers imply something about ownership, and falsely implying that can be confusing. So if your function takes a shared_ptr, be sure to document whether it can retain a reference or not.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
6

In many situations, I believe they are definitely the way to go (less messy cleanup code, reduced risk of leaks, etc.). However there is some very slight extra expense. If I were writing some code that had to be as fast as possible (say a tight loop that had to do some allocation and a free), I would probably not use a smart pointer in hopes of eking out a bit more speed. But I doubt that it would make any measurable difference in most situations.

Mark Wilkins
  • 40,729
  • 5
  • 57
  • 110
  • 7
    You'd probably use an virtually-zero-overhead scoped_ptr if you were allocating, using and destroying resources in a tight loop (which might not be a good idea in the first place). Choose the right kind of smart pointer for the task. – visitor Mar 16 '10 at 12:33
  • If you do need to delete the object there is no actual performance penalty with some smart pointers (in particular `scoped_ptr`) – David Rodríguez - dribeas Mar 16 '10 at 12:35
  • @David: That's a good point ... although I was thinking (possibly out of ignorance) that there would still be one extra test in that situation. I'll have to dump some assembly and teach myself. – Mark Wilkins Mar 16 '10 at 12:51
  • 3
    `shared_ptr` have overhead both in the allocation of the shared information block and checks for whether to deallocate based on that shared information. But with single ownership smart pointers, the destructor does not need to perform any test: just delete the internal pointer. Examples: libstdc++: `~auto_ptr() { delete _M_ptr; }`, boost 1.37: `~scoped_ptr() { checked_delete(ptr); }` where `checked_delete` is a compile time check for type completeness and a single call to `delete`, that will most probably be inlined. – David Rodríguez - dribeas Mar 16 '10 at 13:24
  • @David: Cool - thanks for sharing that. I learned something. – Mark Wilkins Mar 16 '10 at 13:26
5

In general, no you cannot use smart pointers always. For example, when you use other frameworks that don't use smart pointer (like Qt), you have to use raw pointers too.

jopa
  • 1,109
  • 7
  • 6
2

If you are handling a resource, you should always use RAII techniques, with in the case of memory means using some form or another of a smart pointer (note: smart, not shared_ptr, choose the smart pointer that is most appropriate for your specific use case). It is the only way to avoid leaks in the presence of exceptions.

There are still cases where raw pointers are necessary, when resource management is not handled through the pointer. In particular they are the only way of having a resettable reference. Think of keeping a reference into an object whose lifetime cannot be explicitly handled (member attribute, object in the stack). But that is a very specific case that I have only seen once in real code. In most cases, using a shared_ptr is a better approach to sharing an object.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
2

My take on smart pointers: GREAT when it's hard to know when deallocation could happen (say inside an try/catch block, or inside a function that calls a function (or even a constructor!) that could throw you out of your current function), or adding better memory management to a function that has returns everywhere in the code. Or putting pointers in containers.

Smart pointers, however, have a cost that you might not want to pay all over your program. If memory management is easy to do by hand ("Hmm, I know that when this function ends I need to delete these three pointers, and I know that this function will run to completion"), then why waste the cycles having the computer do it?

RyanWilcox
  • 13,890
  • 1
  • 36
  • 60
  • 3
    "Hmm, I know that when this function ends I need to delete these three pointers, and I know that this function will run to completion" - that's what `auto_ptr` is for, or `scoped_ptr`. It'd be rare for them to create measurable overhead, and meanwhile they make it easier to get the code right. For example, if acquiring the second of those three pointers throws an exception, do you free the first? How much code do you have to write to do so, compared with using smart pointers? How often do you really acquire resources that *you* need to free, but where *your* acquisition cannot fail? – Steve Jessop Mar 16 '10 at 14:06
  • That's another good example of being inside a function that could throw you out of your current function, and good point :) – RyanWilcox Mar 16 '10 at 16:42
1

Yes BUT i have gone several projects without the use of a smart pointer or any pointers. Its good practice to use containers such as deque, list, map etc. Alternatively i use references when possible. Instead of passing in a pointer i pass a reference or const reference and its almost always illogical to delete/free a reference so i never have issue there (typically i create them on the stack by writing { Class class; func(class, ref2, ref3); }

0

It is. Smart pointer is one of the cornerstones of the old Cocoa (Touch) ecosystem. I believe it keeps impacting the new.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Trombe
  • 197
  • 3
  • 8