18

What are some of the main reasons to use raw pointers in 2014, given that the C++11 standard is now well supported by most decent compilers?

I identified a couple of scenarios :

  1. You are extending a legacy codebase that makes heavy use of raw pointers, and you would like to maintain consistency in style.

  2. You are using a library that only exports raw pointers, but I guess you could still make use of casts.

  3. You want to exploit pointers's capability to provide multiple levels of indirection. (I do not know C++11 well enough to know if this can be achieved using smart pointers, or using some other techniques.)

What other scenarios do you think are appropriate for use of pointers?

Would you even recommending learning about pointers in general, today?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Gumbly jr.
  • 729
  • 2
  • 9
  • 18
  • 1
    The related http://stackoverflow.com/q/22146094 might cover this question as well. From Joseph Mansfield's answer: "The second [question] is when should we use pointers?" – dyp Sep 06 '14 at 22:43
  • 1
    Q: Would you even recommending learning about pointers in general, today? A: I'd recommend learning about pointers, C programming, assembly programming and one or more good scripting languages (e.g. Python or Lua) ... long before I'd touch a monstrosity like C++11. – FoggyDay Sep 06 '14 at 22:46
  • Hmya, it seems somewhat inevitable that a lot of C++ programmers are going to start writing Visual Basic style code. The kind that was around 20 years ago before it got a garbage collector. Well, it was *very* popular ;) – Hans Passant Sep 06 '14 at 22:49
  • @HansPassant, is that a bad thing? – Gumbly jr. Sep 06 '14 at 22:50
  • No it is not. It is only bad when everything starts to look like a nail. – Hans Passant Sep 06 '14 at 22:53
  • @HansPassant, Sir, I don't get you. What is a nail in this context? – Gumbly jr. Sep 06 '14 at 22:54
  • 2
    Maslow's hammer: http://en.wikipedia.org/wiki/Law_of_the_instrument – Hans Passant Sep 06 '14 at 22:57
  • 5
    @FoggyDay: Learning the portions of C++ that it shares with C is a fine idea, but that absolutely should be done using a C++ compiler, not actual C. The C type system is an unsafe joke. Parts of it have been fixed (no more implicit `int`), but parts are still an invitation to disaster. There are a couple C features that are nice to have (named initializers and `restrict`) but they don't begin to compensate for the problems. – Ben Voigt Sep 06 '14 at 22:58
  • 4
    @FoggyDay C++11 is not a 'monstrosity'. Learning assembly is not a must have skill to become a good programmer, there should be no reason for most (90%+) of people to learn assembly. A lot don't and they get by just fine. – Rapptz Sep 06 '14 at 22:59
  • 1
    @Puppy: Tell that to my clients. They pay me good money to have those skill sets. – Michael Petch Sep 06 '14 at 23:14
  • Bad puppy! BAD puppy! – Jim Balter Sep 06 '14 at 23:28
  • on the desktop, c++11 is everywhere, but that is not the case for many embedded platforms. even platforms that claim to support c++ don't necessarily have the entire c++11 implemented. so there's another reason for you. and yet another one is if you need access to absolute addresses. – thang Sep 08 '14 at 12:27

6 Answers6

19

I can imagine circumstances where you have a statically-allocated array and you want to use a raw pointer to iterate through it in high-performance code. There's still nothing wrong with this.

Your #1 is true.

Your #2 is possibly not right: if you're "making use of casts" to transform raw pointers owned by a third-party library, into smart pointers (implying local ownership) then something has gone horribly wrong.

Your #3 is technically true but avoid this whenever you can.

What is not recommended nowadays is playing with raw pointers to your own, dynamically-allocated memory. That is, the advice is to avoid new without smart pointers (and the corollary is that you shouldn't need delete).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 3
    +1, especially last paragraph. – CodeAngry Sep 06 '14 at 22:46
  • I think #1 is opinion-based. It sounds like you have to trade "consistency in style" off against "advantages of other approaches". – dyp Sep 06 '14 at 22:46
  • *"What is not recommended nowadays is playing with raw pointers to dynamically-allocated memory."* It's a general-purpose tool, so specialized solutions are better-suited to any specific problem. However, some corner cases always exist. E.g. http://coliru.stacked-crooked.com/a/a6c607514601b013 – dyp Sep 06 '14 at 22:50
  • 1
    I don't see how "third party library exports raw pointers" equates to "third party owns the object". Plus, that's why `std::unique_ptr` supports deleters, so the object can be handed back to the library for cleanup. – Ben Voigt Sep 06 '14 at 23:03
  • When you say #3 should be avoided, is that because there is a better way of achieving indirection, or simply because there is no need for indirection in getting things done? – Gumbly jr. Sep 06 '14 at 23:04
  • @dyp: I don't see any advantage to having a raw pointer to that lambda. Why not just use a `std::function` member of the class, which cannot fail to be deleted simply because an exception was thrown, thread creation failed, or etc. – Ben Voigt Sep 06 '14 at 23:07
  • @BenVoigt The raw pointer to the lambda (from `new auto`) is required for the `void*` interface of `pthread_create`. It is a run-time decision who deletes the lambda (once you introduce error handling for the thread creation), so I don't think you get the benefits of a `unique_ptr` for the same purpose (`shared_ptr` would work, but has overhead). I don't quite understand how you want to use `std::function`. – dyp Sep 06 '14 at 23:31
  • @dyp: Pass `this`, use `reinterpret_cast` to recover it and pull out the functor from the member variables. No lifetime management needed, since the main application is already required to manage the lifetime of the thread object in order to join it later. The functor can optionally be swapped with an empty one in order to free the extra state early, but failing to do so won't result in an accidental leak. – Ben Voigt Sep 06 '14 at 23:34
  • @Ben: _"I don't see how 'third party library exports raw pointers' equates to 'third party owns the object'"_ The key word in my answer is _if_ (also the _possibly_). _"Plus, that's why `std::unique_ptr` supports deleters"_ Granted. – Lightness Races in Orbit Sep 06 '14 at 23:38
  • @BenVoigt You can detach a thread object (not this one, but this is only a sketch of a `std::thread` implementation); the data passed to the other thread should not be associated with this object. -- The whole thing was meant as an example of a technique rather than a best-practice demo. When you need to create something at one point, and delete it at one specific other point in your program, the bare-bones dynamic memory management can still be an acceptable solution IMHO. – dyp Sep 06 '14 at 23:38
  • @dyp: If you're going to argue against someone's idea of best practice, with an example that you describe as "not best-practice", then I'm really sure what your point is tbh. – Lightness Races in Orbit Sep 06 '14 at 23:45
  • @BenVoigt Hmm you might be right, rewriting this using `unique_ptr` [is not really that painful](http://coliru.stacked-crooked.com/a/ca6e5793f7317701); compare to [the manual solution](http://coliru.stacked-crooked.com/a/ce04160246f8bb41). -- @Lightness I did not want to present best practice, but an corner case where smart pointers are not the best solution (here: conditionally passing ownership). I've failed; there might be better examples. -- Edit: Although.. it seems there's no move-elision in the first version. – dyp Sep 06 '14 at 23:58
19

Everybody is against raw pointers as it's way too easy to leak them.

But you can use raw pointers to point to data owned somewhere else... just don't new/delete them. Use std::unique_ptr or std::shared_ptr for that. Or for dumb (POD) memory buffers use std::vector<unsigned char>, don't malloc and free yourself.

Think of a std::vector<heavy_object*> when you need to juggle with sub-selections of objects that are non-trivial to copy but already exist elsewhere. You need pointers for this.

You also need pointers in functions for optional arguments where references don't cut it as you want to be able to pass a nullptr.

Pointers to consecutive objects can also be iterated easily without any std::iterator overhead. Just ++ or -- and that's it. I often use direct pointer iteration instead of begin, end for vectors.

As you understand how they work... you'll need them a lot and you'll use them properly.

CodeAngry
  • 12,760
  • 3
  • 50
  • 57
  • 1
    Thanks, this was helpful, good point on nullptr. – Gumbly jr. Sep 06 '14 at 23:02
  • 1
    Optional arguments should be `std::optional` nowadays. Introducing indirection for these is an anti-pattern. I'm also curious as to what "overhead" you think there is with iterators. – Lightness Races in Orbit Sep 06 '14 at 23:39
  • @LightnessRacesinOrbit I come from a C background. Whenever I can and data is in a row, I use pointers to walk it. I don't think that's gonna change any time soon. I don't know of overhead but I do know that pointers have the least of it... And until I know everything that happens in the C++ Standard, I'll just go near the metal with C style use. I know it's the insecurity of the lesser coder that makes me do this but it served me well. – CodeAngry Sep 07 '14 at 00:26
  • 4
    Your personal preference is one thing, but asserting that iterators have an overhead when you freely admit you have no idea whether this is true... is just irresponsible. – Lightness Races in Orbit Sep 07 '14 at 01:24
  • Regarding the vector: this could easily be replaced with a vector of shared_ptr's right? Just so I get the concept right – Calle Bergström Oct 04 '17 at 07:24
10

Smart pointers are used for handling object ownership issues, but not all pointers are used to deal with object ownership. For example it makes more sense to pass raw pointer to a function if you are not planning to pass ownership to this function (i.e. you just want the function to deal with the data addressed by the pointer)

JarkkoL
  • 1,898
  • 11
  • 17
8

IMHO Raw pointers still have their place.

What C++11 gives us is the ability to manage the lifespan of raw pointers so that we don't have to delete them ourselves.

There is nothing wrong with using raw pointers as long as they are managed by a smart pointer/pointer manager in the correct scope or frame to ensure their lifespan is correct. If that is true then you never have to delete a raw pointer and you can safely use them within the scope/frame throughout which their life-time is guaranteed.

I would say, if possible, store a raw pointer to your new object in a std::unique_ptr if its lifespan should be controlled by a given scope/frame. Once that is done use the raw pointer within that scope frame. Just never delete it;

Sometimes it is not possible to manage the lifespan of a new object from a single scope or frame. In this case use a std::shared_ptr in every scope/frame that independently needs to manage the lifespan of the new object. Then, within each scope/frame, there is no reason not to use the raw-pointer just like when it is being managed by a std::unique_ptr.

So there is often no reason to incur the speed disadvantage of smart pointers as one of their strengths lies in managing the life-span of the new object in order to ensure the validity of the raw-pointer and the automatic destruction of its object when its no longer needed.

There are other times when a raw pointer is not appropriate.

For example when a managed pointer needs to transfer "ownership" to another scope/frame. That is when you need the scope/frame responsible for managing the life-span of the new object to change. In these cases avoid raw pointers like the plague!

Galik
  • 47,303
  • 4
  • 80
  • 117
2

What other scenarios do you think are appropriate for use of pointers?

One of the main scenarios in which raw pointers are used is when you have non-owning pointers. Typically, where a reference would work, but you want to avoid the constraints of a reference (non-reseatable, non-copyable). You could use a reference_wrapper type in those cases, but it's simpler to just use a raw pointer instead. Smart-pointers encode ownership (who creates and destroys the object), so, if there is no ownership to encode (because it is implied otherwise), then a raw pointer is OK.

Just to make it clear, typical examples of what I just explained are things like:

  • temporary copyable functors that need a pointer to some object that it doesn't own.
  • internal cross-links within a data structure (e.g., "back pointers").

But it's important to notice that these things should not, in general, be present in interfaces. Generally, you can avoid raw pointers pretty much completely in interfaces (e.g., library functions and classes), and only really use them internally, i.e., in library-side code, not in user-side code. In other words, if you need to use raw pointers, hide them away.

Raw pointers are also sometimes seen for optional function parameters, where you can pass in a nullptr if you don't want that result.

The main thing that should be avoided, and can be avoided in general, is naked new / delete calls in user-side code. A typical good modern C++ library (and even more so with C++11) will not have any such naked new / delete occurrences, and that's a fact.

Raw pointers are not so much a problem by themselves, what is problematic is (1) manual memory management, and (2) ownership management (which is problematic if raw pointers are used instead of smart-pointers).

Would you even recommending learning about pointers in general, today?

Of course you should learn about pointers. They are essential to understanding programming, and to learning to write library-side code. Raw pointers are still very present in the guts of a lot of library code and such, even if you don't see them.

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
  • 1
    I agree with most of the answer (especially the emphasis on "only bad for memory management"), but 1. `std::shared_ptr` and `std::weak_ptr` don't encode ownership. 2. You can copy a reference with `int& my_copy = other`, or `my_copy(other)` into an object. 3. I think for a lot of cases where you might use reseatable references, you are iterating, so you can use `for(int& x : thing)` instead. Maybe in most cases, a need for a reseatable reference is an opportunity to write higher-level code. 4. For an optional parameter, you can sometimes use a default instead. – leewz Oct 25 '15 at 20:37
-2

common reasons to use raw pointers off the top of my head.

  1. Reading and parsing binary files
  2. Interacting directly with hardware
  3. Arrays?
  4. Speed. AFAIK Smart pointers are slower than raw pointers and there are relatively safe ways to use raw pointers. See Chromium's code base for example or WebKit's. Both use various kinds of tracked memory without the full overhead of smart pointers. Of course with limitations as well.
gman
  • 100,619
  • 31
  • 269
  • 393
  • Smart pointers are only slower than raw pointers when they do more. C++ compilers are pretty good at making `std::unique_ptr` compile to the same thing as raw pointer plus manual cleanup... with the advantage that you can't forget to do the cleanup on one of the return paths. – Ben Voigt Sep 06 '14 at 23:01
  • Are we talking theoretically or for real? What's `sizeof((SomeObject*)[100])` vs `sizeof(smart_ptr[100])`? What's the time difference between `new (SomeObject*)[10000]` and `new (smart_ptr)[10000]`? – gman Sep 06 '14 at 23:05
  • 1
    `sizeof` should be the same. Allocation should be the same as if you value initialized the block of raw pointers. But I am comparing apples to apples; it's easy to start comparing apples to oranges by e.g. comparing `std::shared_ptr` to code that uses raw pointers and doesn't correctly count references. – Ben Voigt Sep 06 '14 at 23:10
  • Maybe I should have said "how much memory" vs sizeof. One is taking more memory than the other. sizeof isn't showing that overhead. On top of that, unless I'm missing something, you can't pass ownership with a shared_ptr unless you pass a shared_ptr. At which point you've added referencing and dereferencing that wasn't there before. With systems like those in Chromium/WebKit you can create a new reference from a raw pointer so you can avoid the ref/def until you actually need it. I'd argue that is comparing apples to apples. – gman Sep 06 '14 at 23:26
  • 1
    Why is one taking more memory? `std::unique_ptr` should take exactly as much memory as `T*`. – Ben Voigt Sep 06 '14 at 23:28
  • 2
    "Smart pointers are slower than raw pointers" - while this does apply to some smart pointer classes (e.g. `shared_ptr` you mention), it does not apply to all of them. You may want to read up on the new shiny [C++11 `std::unique_ptr`](http://en.cppreference.com/w/cpp/memory/unique_ptr), which is designed to be as lean as possible (essentially it calls `delete` in the destructor, and nothing more - it's not a reference counted pointer) – milleniumbug Sep 06 '14 at 23:32
  • unique_ptrs have the same issues. You can't pass them around. With a shared_ptr you can pass them but with the added overhead with each def/ref. With unique_ptr you can't pass them. You can only pass the raw pointer, no one else can take ownership unless you pass unique_ptr refs. With RefCounted style you pass raw pointers. anytime you want a reference you get can convert the raw pointer into a new reference. You have most of the benefits and none of the drawbacks. You don't have some funcs that take a shared_ptr, others take a unique, others take a raw. You just have raw. Easy, simple, fast. – gman Sep 07 '14 at 00:30
  • In other words, back to the original quesiton. There are still legit uses of raw pointers. smart_ptr and unique_ptr solve some problems but add certain tradeoffs. RefCounted also has tradeoffs. As does raw. Those are real tradeoffs. – gman Sep 07 '14 at 00:32