30

About the usage of new and delete, and Stroustrup's advice...

He says something like (but not exactly, this is from my notes of his book):

A rule of thumb is that, new belongs in constructors and similar operations, delete belongs in destructors. In addition, new is often used in arguments to resource handles. Otherwise avoid using new and delete, use resource handles (smart pointers) instead.

I was wondering if the more experienced folks with C++11 have really applied this or not.

My impression of this was, wow this seems like a really cool rule to follow. But then I got suspicious, as for any general rule. At the end of the day you will end up using new and delete wherever necessary. But maybe this rule is a good guideline I don't know.

Jack
  • 125,196
  • 27
  • 216
  • 324
user3111311
  • 6,153
  • 5
  • 29
  • 40
  • 6
    “At the end of the day you will end up using new and delete wherever necessary.” Fortunately, it isn’t necessary. :) – rightfold Dec 30 '13 at 22:14
  • I haven't had yet the time with C++11, but I would like it for this rule to turn out useful too :) – user3111311 Dec 30 '13 at 22:15
  • 5
    @user3111311 I've generally followed this rule with the help of the boost libraries for the last decade, and with the help of in-house libraries before that. C++11 just standardized the established practice. – Cubbi Dec 30 '13 at 22:17
  • If I am correct, the major idea is to put new and delete in the very low levels, and to remove them from the higher level code that is closer to the user. – user3111311 Dec 30 '13 at 22:18
  • 1
    Unrelated to this Q, but I wanted to answer on the implicit conversion Q (now deleted): I've been bitten by unexpected implicit conversions more than once, so yes, I'd recommend "explicit as default". When considering a ctor `Foo(const Bar &)`, I always ask myself "If a function takes a `Foo` parameter and I pass a `Bar` object, do I want that to work silently, or is it more likely to be a programmer oversight/misunderstanding?" Usually, it turns out to be the latter. Implicit conversions are fine for e.g. mutable-to-const iterator, but most of the time, I find I want not to have them. – Angew is no longer proud of SO Jan 06 '14 at 14:21
  • With the downplay of the `new` and `delete`, maybe promoting (de)allocation from library functions to operators was a mistake.... – CTMacUser Jan 08 '14 at 06:31

3 Answers3

48

It's a great rule. In fact, you can avoid using new in arguments to smart pointers by using the appropriate make_ functions. For example, instead of:

std::shared_ptr<int> p(new int(5));

You can often do:

auto p = std::make_shared<int>(5);

This also has the benefit of being more exception safe. While a std::make_unique doesn't yet exist, it is planned to make its way into C++14 (it is already in the working draft). If you want it now, there are some existing implementations.

You can go a step further and even avoid using new and delete in constructors and destructors. If you always wrap dynamically allocated objects in smart pointers, even when they're class members, you won't need to manage your own memory at all. See the Rule of Zero. The idea is that it's not the responsibility of your class to implement any form of ownership semantics (SRP) - that's what the smart pointers are for. Then you theoretically never have to write copy/move constructors, copy/move assignment operators or destructors, because the implicitly defined functions will generally do the appropriate thing.

Community
  • 1
  • 1
Joseph Mansfield
  • 100,738
  • 18
  • 225
  • 303
  • 4
    +1 For menctioning the rule of zero. Make default ctors and assigment operators working is one of the best benefits of smart pointers and resource handles in general. I don't remember the last time I wrote a destructor. If you write classes in this way (resources managed by handlers) you avoid a lot of common C++ headaches about the bunch of different ctors, assignment operators, etc a class could have (And this problem is worst in C++11, where The Rule Of Three is The Rule Of Five) – Manu343726 Jan 02 '14 at 11:25
  • If you're using `unique_ptr`s for class members (which I agree is better than raw pointers if your class has exclusive ownership), won't you have to write your own copy ctor/assignment operator? – Shea Levy Jan 02 '14 at 15:09
  • 1
    @SheaLevy Depends on whether you want a deep or shallow copy. For a shallow copy, you shouldn't want a copy constructor/assignment operator because that would break the unique ownership (and this is the default behaviour). If you want a deep copy, you will need to copy the pointed-to object and have the new `unique_ptr` point at it, which will involve implementing a copy constructor/assignment operator. You won't need to do any memory management though, which is the important thing. – Joseph Mansfield Jan 02 '14 at 15:15
14

Seems more like a poll than a question but here it goes: in application code I generally don't use new at all. Due to our coding guidelines the code does use pointer but none of these "naked" pointers is actually transfering ownership. All objects are owned by some other object.

To be fair, when objects need to be allocated the allocation generally uses something morally equivalent to std::make_shared<T>(...) which sometimes does show up in application code. One major reason for this rather thorough absence of new (or similar) is that objects are generally allocated using stateful allocators and not doing so via a resource manager actually happens to be fairly complicated. Thus, there is little place for direct memory allocation using new or a placement version thereof in application code.

In some infrastructure code, especially when creating custom containers the situation is slightly different: there is memory allocated (from allocators and initialized using placement new) there. However, even there any result from memory allocation and initialization of objects is immediately passed on to resource managers. Basically, I can't cope with explicit resource management and using resource managers just reliefs me of the necessary work.

Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356
3

The way I think of it is that every resource should be owned by something. The owner is the one who is responsible for cleaning up. Usually this owner is a smart pointer of some kind, but even std::vector is an owner a resource: the block of memory which stores it's contiguous elements. This advice holds not just for memory but any resource such as file descriptors, database handles, mutexes, etc...

When you call new and delete manually in some part of your code that's not a wrapper class, you the programmer become the resource owner. With ownership comes the responsibility of cleaning up after yourself. Now you and all of the maintenance programmers who come after you have to ensure that all code paths after the new eventually lead to a delete. Even for simple functions this very is easy to get wrong. With exceptions, almost impossible unless you carefully wrap everything in try catch blocks, resulting runtime performance penalties and polluting your code with extra scopes and unnecessary exception logic. Finally, even if you do get it right, you just wasted a lot your time doing this tedious work of resource management. The compiler is tool which can do this work for you, use it.

The worst situation is when some subsystem allocates a resource, it gets passed around the application, and some other far away subsystem frees it. The number of possible code paths in this situation is intractable. It is very difficult if not impossible for a human being to reason about and trust. In my opinion, this style of programming is unmaintainable. How many C projects have you worked with in the past that are riddled with memory errors, especially on rarely if never executed error handling paths? I've dealt with more than I care to see anymore.

C has manual memory management, Java and others have garbage collection. C++ has RAII. It's as efficient as C and and almost as safe as garbage collection.

My rule is simple, if you find yourself manually cleaning up any resource, you have just written a bug.

Matthew Fioravante
  • 1,320
  • 13
  • 18