4

Are there any reasons to still use raw pointers (for managed resources) in C++11/14?

Should resource member variables in a class be held in their own smart pointers for automatic RAII without need for cleanup in destructor?

Is the implementation of smart pointers inlined that there is no overhead in doing so?

SN1
  • 43
  • 3
  • 5
    The general advice is to never use `new` or `delete`. There might be a case where RAII just doesn't work well, but for the most part, no raw pointers owning resources. You could argue that anyway by having a class that owns the resource, but not employing RAII if it won't work. – chris Jun 25 '14 at 16:27
  • 2
    unique_ptr has virtually no overhead, shared_ptr still has some, which is necessary for shared-ownership semantics. – T.C. Jun 25 '14 at 16:42
  • 3
    @chris I don't know where you got that "general advice" from. `new` and `delete` are like everything else: you should use them when they solve the problem, and not use them when they don't. – James Kanze Jun 25 '14 at 16:43
  • @T.C. I'm not sure what the situation is now---I haven't checked in a long time. But until not so long ago, a compiler wouldn't put an object of class type in a register, especially if it had member functions. So passing a smart pointer as a argument, or returning it, meant constructing it in memory; raw pointers are often passed in registers. (I'd say usually, except that there are some register poor architectures where this isn't the case.) – James Kanze Jun 25 '14 at 16:45
  • Passing or returning a unique_ptr by value means transferring ownership. That hopefully won't happen very frequently in performance-critical code. – T.C. Jun 25 '14 at 16:55
  • 1
    @JamesKanze, Herb Sutter and some figures like that, as well as StackOverflow in general. Of course there are always valid reasons to use something, such as some necessary performance, but RAII is a good enough solution that it's what we teach instead of carefully having to make sure the code is exception-safe and has no other leaks. It's just so much easier to get right and the code ends up being shorter as a bonus. The never part comes from that and the addition of `std::make_unique`. At least all I've heard lately is the advice changing from only then to never. – chris Jun 25 '14 at 17:00
  • Herb Sutter has explained a lot of things very clearly, but he has also responsible for a number of anti-patterns. The automatism of using smart pointers, for example, is one of them. There are cases where smart pointers are appropriate (and using smart pointers in his vector may well be one of them), but they certainly shouldn't be used universally. In many types of applications, for example, most `delete` will be `delete this`. (There is certainly an argument to be made that `new` is used far too much; that in many uses we see, it would be better to forgo dynamic allocation completely.) – James Kanze Jun 26 '14 at 08:44

3 Answers3

5

Are there any reasons to still use raw pointers (for managed resources) in C++11/14?

I assume that by "managed resources" you mean "owned resources".

Yes there are reasons:

  1. As you are inferring in your question, sometime you want to refer to an object or none, and it can change through time. You have to use a raw pointer in this case, because there is no alternative right now in this specific case. There might be later as there is a proposal about adding a "dumb" non-owning pointer which just clarify the role of the pointer (observe/refer, not own). Meanwhile the recommendation is to avoid new/delete if you can and use raw pointer only as "refers to without owning" kind of re-assignable reference.
  2. You still need raw pointers for implementation of non-raw pointers and any low level RAII construct. Not everybody needs to work on fundamental libraries, but of course if you do, then you need basic constructs to work with. For example in my domain I often have to build up custom "object pool" systems in different ways. At some point in the implementation you have to manipulate raw memory which are not objects yet, so you need to use raw pointers to work with that.
  3. When communicating with C interfaces, you have no other choices than to pass raw pointers to functions taking them. A lot of C++ developers have to do this, so it's just in some very specific regions of your code that you will have to use them.
  4. Most companies using C++ don't have a "modern C++" experience of C++ and work with code that use a lot of pointers where it is not actually necessary. So most of the time when you add code to their codebase, you might be forced by the code-environment, politics, peer-pressure and conventions of the company to use pointers where in a more "modern c++" usage kind of company it would not pass peer-review. So consider the political/historic/social/knowledge-base-of-coworkers context too when choosing your techniques. Or make sure companies/projects you work in match your way of doing things (this might be harder).

Should resource member variables in a class be held in their own smart pointers for automatic RAII without need for cleanup in destructor?

Resource member variables, in the best case, should just be members without being obvious pointers, not even smart pointers. Smart pointers are a "bridge" between code manipulating raw pointers and pure RAII style. If you have total control over some code and it's new code, you can totally avoid making any use of smart pointer in your interfaces. Maybe you will need them in your implementations though. Keep in mind that there is no actual rules, only recommendations of what you could result in if you

Is the implementation of smart pointers inlined that there is no overhead in doing so?

Implementation of standard smart pointers is as efficient as they can be so yes most of their code is inlined. However, they are not always free, it depends on what they actually do. For example, in almost all cases, unique_ptr is exactly one raw pointer, with just additional checks around it's places of use. So it's "free". shared_ptr on the other hand have to maintain a counter of how many other shared_ptr refer to the same object. That counter can be changed on several threads doing copies of the shared_ptr, so it have to be atomic. Changing the value of atomic counters is not always free, and you should always assume that there is a higher cost than copying a raw pointer.

So "it depends".

Just:

  • use RAII as much as you can, without exposing any kind of pointer in your interfaces (smart or not);
  • use standard smart pointers in implementations if you must use owning pointers;
  • use raw pointers only if you need to refer to object, null, or other objects changing through time, without owning them;
  • avoid raw pointers in interfaces except in case of allowing passing optional objects (when nullptr is a correct argument);

You will end-up with code that, from the user's perspective, don't seem to manipulate pointers. If you have several layers of code following these rules, the code will be easier to follow and highly maintainable.

On a related note from: When to use references vs. pointers

Avoid pointers until you can't.

Also note that Sean Parents in his recent talks also consider smart pointers to be raw pointers. They can indeed be encapsulated as implementation details of value-semantic types corresponding to the actual concept being manipulated. Additionally using type-erasure techniques in implementations but never exposing them to the user helps extensibility of some library constructs.

Community
  • 1
  • 1
Klaim
  • 67,274
  • 36
  • 133
  • 188
0

It depends. If the object is fully owned, constructed and destructed by another object, there is a good case for using an std::unique_ptr in that other object. If you have an object which owns several such objects, all of which are dynamically allocated in the constructor, then you have to do something; if the semantics of the usual smart pointers isn't appropriate (which is often the case), then you'll have to invent something: in the case of a graph, for example, you might put the root pointer in a base class (initializing it to null), and have the destructor of the base class clean-up the graph, starting at the root.

Of course, unless your class has some sort of dynamic structure like a graph, you might ask yourself why it is using dynamic allocation to begin with. There are special cases (where, for example, the owned object is polymorphic, and its actual type depends on arguments to the constructor), but in my experience, they aren't that common. In practice, there just aren't that many cases where a smart pointer can be used in an object, much less should be used.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

RAII is more than just wrapping up new and delete - a smart pointer is a form of RAII but RAII is a lot more than that. A good candidate for RAII is when you have any sort of mirroring functions: new/delete, initialise/teardown, stop/start. So your resource should still have its own RAII class - one that performs its cleanup function in its own destructor.

DanDan
  • 10,462
  • 8
  • 53
  • 69