7

This question already has an answer here:

C++ tools and libraries have become more powerful than ever.

For instance, we can replace arrays by vectors. We can replace pointers by references. We can use smart-pointers.

Is it a common practice among professional programmers to avoid the use of raw pointers?

marked as duplicate by Robert Harvey, durron597, gnat, Pieter B, GlenH7 Aug 7 '15 at 16:04

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 5
    We can use smart-pointers from Boost library. are the smart_ptr's in the stdlib not good enough? – tkausl Aug 7 '15 at 0:38
  • @tkausl, corrected my question. – user36522 Aug 7 '15 at 0:41
  • But smart pointers are pointers too. Do you mean "raw owning pointers" when you say "pointers"? – zenith Aug 7 '15 at 9:26
  • @zenith, yes. you got it right. – user36522 Aug 7 '15 at 9:39
  • 1
    I think this question is ambiguous because avoid can have two meanings: "try to not use yourself" and "try to not encounter". Like me: yes I avoid using pointers (in my own new code), no I don't avoid (run away from) code that uses pointers. – Pieter B Aug 7 '15 at 14:03
11

If you are starting a new green field project that is at a high enough level (as in you're talking about writing an enterprise Foo rather than a device driver) and has no external dependencies you can probably avoid using pointers entirely. But most development isn't brand new development, it's leveraging the millions of existing lines of C++ code out there which will undoubtedly include pointers. And even new applications almost always rely on external libraries and other APIs that will frequently use pointers. So the set of applications that can be built completely without pointers is pretty small.

It may be possible to minimize how much of your code base needs to use pointers. You may be able to find different libraries that don't rely on pointers (though this generally comes at a cost in performance or functionality). You can create your own interface on top of existing code and libraries that depend on pointers and use that new interface in the majority of your new code base. But you're almost always going to sacrifice some performance and/or flexibility when you start adding in new layers. Whether those trade-offs are acceptable to you will depend heavily on your application.

8

As humans, we are fallible and prone to making errors. Therefore, we should try to write our programs as safe as possible, and let tools like the compiler or Valgrind help us find problems before they lead to crashes in production.

It is clear that it is impossible to avoid pointers at all levels – they provide a mechanism for indirection that is invaluable for complex architectures – “all problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections” (Wheeler's law). However, raw pointers are too powerful for either us or the compiler to efficiently reason about.

  • Pointers might be NULL.
  • Pointers might reference invalid memory, freed memory, or unallocated memory. Hello, segfault!
  • A pointer might point to a single value, or be array-like and point to a sequence of values. In the latter case, there might not be a clear length associated with the array-like pointer, which encourages buffer overruns.
  • Pointers are difficult for the compiler to optimize.
  • The lifetime of pointed-to values is difficult to manage: Who is responsible for deleting the value? What happens with other pointers referencing that memory?

C++ offers a number of mechanism that allows a programmer to more precisely indicate why they are using a pointer-like thing.

  • References are guaranteed to be non-null, but can still have problems regarding lifetime. They are best used for function parameters, and effectively subsume the C patterns of pass-by-pointer (in contrast to the default pass-by-value) and out-parameters.
  • Iterators are an array-like pointer that points to some element in a collection. Whenever you are tempted to increment a pointer, you actually wanted an Iterator. In practice, Iterators have no or only few problems regarding null pointers, lifetime, or buffer overruns.
  • Smart Pointers solve the problems regarding lifetime, by explicitly specifying lifetime semantics for the value. It is impossible to write exception-safe code with raw pointers in some circumstances, but smart pointers make this trivial. In other respects, they retain the full flexibility of raw pointers – importantly, they can be NULL. Also note that std::unique_ptr is a zero-cost abstraction, so whenever your pointer has a clear owner, using the smart pointer is a clear win in safety.
  • STL types such as std::vector or std::string replace most uses of array-like pointers, and introduce proper lifetime management, proper copying, make buffer overruns less likely with correct use, and aren't even terribly inefficient. Unfortunately, they have an unsafe operator[], but you always have the option to use the bounds-checked .at() instead.

These solutions are not perfect, but they make it way more difficult to write dangerously wrong code. Unless you are optimizing for C compatibility or (in case of shared pointers, some usage scenarios of vectors) object size, there is no reason to forgo this safety. I have yet to see a good example where references, iterators, smart pointers, or collections were not a better, more self-documenting, and more correct solution to a problem than raw pointers.

Is it professional to use these safety features? Yes: it is professional to solve our problems correctly and efficiently. Debugging segfaults, buffer overruns, and memory leaks is not an efficient use of my time, so I'd rather avoid them from the start. With any half-decent IDE or editor (and C++11's auto), this isn't even much more code to type.

  • 1
    Note that when using STL iterators with STL containers, it is a common mistake to pass in a pair of iterators that don't belong to the same container, resulting in crashes. Another common mistake is write-past-end. A conceptual understanding of STL container's implementation (which requires an understanding of pointers) will be needed to help avoid these mistakes. – rwong Aug 7 '15 at 8:20
  • 3
    +1 for "raw pointers are too powerful". That's exactly the problem: they do too much. They do not follow the Single Responsibility Principle. Therefore they are hard to manage and reason about. One has to read more code to know what a particular raw pointer variable is used for. – zenith Aug 7 '15 at 9:35
3

No, they are not avoided per se.

Use "raw" pointers as appropriate, with a long list of things where they are not appropriate.

To keep this short:

  • Do not use raw pointers to model ownership (avoid naked deleteand possibly also naked new -> make_sharedor make_uniqueare your friends)
  • Do not use raw pointers to "pass around" a conceptual non-owning reference iff a C++ reference suffices.
  • C++ references do have some restrictions where using raw pointers is OK and often the easier way (to write, read and reason about).
  • I don't see anything wrong with modelling optional function parameters by using raw pointer arguments, e.g. f(int* pOptValue) where the user can pass nullptr to indicate no-value. (Although I'm sure a lengthy discussing of using optional<int> instead could be started.)