16

Reading through an old C++ Journal I had, I noticed something.

One of the articles asserted that

Foo *f = new Foo();

was nearly unacceptable professional C++ code by and large, and an automatic memory management solution was appropriate.

Is this so?

edit: rephrased: is direct memory management unacceptable for new C++ code, in general? Should auto_ptr(or the other management wrappers) be used for most new code?

Paul Nathan
  • 39,638
  • 28
  • 112
  • 212
  • 16
    Questions like this make me feel like I'm working on a different planet from everyone else. All the answers below suggest that new/delete is obsolete, dangerous, evil, deprecated; at my (large) company, it is basically the *only* mechanism we use for allocating dynamic objects! We have container classes, but they all just call through to new/delete underneath. – Crashworks Jan 11 '10 at 08:49
  • Note: `Foo* f = new Foo` is perfectly valid. – sbi Jan 11 '10 at 12:11
  • 12
    @Crashworks: What they do underneath doesn't matter though. Underneath, your loop or function call is just a goto, but that doesn't mean "gotos are fine to use". Underneath, every pointer and reference is implemented by hacking on memory addresses, but that doesn't mean you can safely treat pointers as memory addresses. Those are abstracted away, in much the same way that new/delete is usually abstracted away behind container classes and RAII wrappers – jalf Jan 11 '10 at 14:32
  • 4
    apropos of nothing: A friend of mine once wrote a compiler with the warning "WARNING: Over-use of meta-syntactic variable 'foo'" (come to think of it, it might have been an error B-) – Brian Postow Jan 11 '10 at 14:58
  • 4
    It may be worth noticing that the question as stated is *not* about merely calling `new` to allocate a Foo. It is about using `new` **and then storing the result in a raw pointer**. And since nothing else is mentioned, I assume that the pointer is not hidden away as a private member in a RAII class either, but is actually passed around and used directly in the application. And *that* is a dangerous practice, whether or not "`new` is evil" – jalf Jan 11 '10 at 20:09
  • @Crashworks Useful smart pointers are fairly new in C++, they have only been around for a couple of years. Before C++11, there was only `auto_ptr`, which was problematic for several reasons, and non-standard Boost. That being said, you don't really need to use smart pointers if you have smart programmers :) Personally I never quite understood how people manage to write so many memory leak bugs all the time. With a minimum of coding discipline, you don't easily write such bugs. – Lundin May 23 '16 at 14:24

10 Answers10

21

This example is very Java like.
In C++ we only use dynamic memory management if it is required.
A better alternative is just to declare a local variable.

{
    Foo    f;

    // use f

} // f goes out of scope and is immediately destroyed here.

If you must use dynamic memory then use a smart pointer.

// In C++14
{
    std::unique_ptr<Foo>  f = std::make_unique<Foo>(); // no need for new anymore
}

// In C++11
{
    std::unique_ptr<Foo>  f(new Foo);  // See Description below.
}

// In C++03
{
    std::auto_ptr<Foo>    f(new Foo);  // the smart pointer f owns the pointer.
                                       // At some point f may give up ownership to another
                                       // object. If not then f will automatically delete
                                       // the pointer when it goes out of scope..

}

There are a whole bunch os smart pointers provided int std:: and boost:: (now some are in std::tr1) pick the appropriate one and use it to manage the lifespan of your object.

See Smart Pointers: Or who owns you baby?

Technically you can use new/delete to do memory management.
But in real C++ code it is almost never done. There is nearly always a better alternative to doing memory management by hand.

A simple example is the std::vector. Under the covers it uses new and delete. But you would never be able to tell from the outside. This is completely transparent to the user of the class. All that the user knows is that the vector will take ownership of the object and it will be destroyed when the vector is destroyed.

Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
7

No.

There are very good reasons to not use automatic memory management systems in certain cases. These can be performance, complexity of data structures due to cyclical referencing etc.

However I recommend only using a raw poiner with new/malloc if ou have a good reason to not use somehting smarter. Seeing unprotected allocations scares me and makes me hope the coder knows what they're doing.

Some kind of smart pointer class like boost::shared_ptr, boost::scoped_ptr would be a good start. ( These will be part of the C++0x standard so dont be scared of them ;) )

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • 2
    Are you assuming that "automatic memory management" only includes reference counting? How about the very simplest one: "Deallocate in the destructor when the object goes out of scope"? That's effectively what `scoped_ptr` does already. How is that not "automatic memory management"? – jalf Jan 11 '10 at 14:45
  • "That's effectively what scoped_ptr does already" That's effectively what normal variables do already. – tstenner Jan 11 '10 at 21:19
  • I'm saying allocations of objects on the heap without some explicit control over the lifetime of that object is error prone and usually a bad idea. I'm not assuming anything in particular about the type of automatic memory management used, just saying that smart pointers are an easy place to start. A bigger list would include reference counting schemes, RAII schemes, object pools etc.. – Michael Anderson Jan 11 '10 at 22:53
7

I think, the problem of all these "...best practices..." questions is that they all consider the code without context. If you ask "in general", I have to admit that direct memory management is perfectly acceptable. It is syntactically legal and it does not violate any language semantics.

As for the alternatives (stack variables, smart pointers etc), they all have their drawbacks. And none of them have the flexibility, the direct memory management have. The price you have to pay for such a flexibility is your debugging time, and you should be aware of all risks.

SadSido
  • 2,511
  • 22
  • 36
  • 1
    Yes, each of the alternatives have individual drawbacks. But taken together, do they not have as much flexibility as "direct" memory management? Is there anything you can do with direct new/delete calls, that can't be achieved by *any* of the alternatives? – jalf Jan 11 '10 at 19:13
  • to jalf: sometimes you want object's destructor to be called *exactly* at the specific place. You cannot achieve it with any kind of shared pointers, because they postpone object destruction until no one points to the object. Weak pointers provide overhead, that may be unacceptable. Scoped pointers can do nothing if you want to extend object's lifetime out of scope. – SadSido Jan 12 '10 at 08:09
6

With some kind of smart pointer scheme you can get automatic memory management, reference counting, etc., with only a small amount of overhead. You pay for that (in memory or performance), but it may be worth it to pay for it instead of having to worry about it all the time.

John
  • 15,990
  • 10
  • 70
  • 110
  • 2
    What's the deal with assuming that "automatic memory management" can *only* refer to reference counting? – jalf Jan 11 '10 at 14:46
  • @jalf: In C++ smart pointers, "automatic memory management" usually means reference counting. Anything else would require infrastructure. – David Thornley Jan 11 '10 at 19:54
  • 2
    Not true. `std::vector vec;` is automatic memory management. All the memory it allocates is cleaned up *automatically*. Think of the `auto` keyword (in its current, pre-C++0x meaning). Why do you think it is called *auto* of all things? Because it *automatically* manages the object's lifetime. All local variables and class members are automatically managed *to begin with*. Automatic memory management is the default. It's only when we jump through hoops and call `new` that we lose it, and have to explicitly create the infrastructure to get it back. – jalf Jan 11 '10 at 20:12
  • By the way, how can you do reference counting without infrastructure? As far as I'm aware, it requires significantly more infrastructure than simply letting scope determine lifetime. – jalf Jan 11 '10 at 20:13
  • jaif: I didn't mean to imply that "automatic memory management" can only refer to reference counting. – John Jan 11 '10 at 21:19
  • may I add that the title is misleading since "new Foo()" is different from "new Foo" ? – andreabedini Apr 01 '11 at 05:00
6

If you are using exceptions that kind of code is practically guaranteed to lead to recource leaks. Even if you disable exceptions, cleaning up is very easy to srew up when manually pairing new with delete.

Nemanja Trifunovic
  • 24,346
  • 3
  • 50
  • 88
4

It depends on exactly what we mean.

  • Should new never be used to allocate memory? Of course it should, we have no other option. new is the way to dynamically allocate objects in C++. When we need to dynamically allocate an object of type T, we do new T(...).
  • Should new be called by default when we want to instantiate a new object? NO. In java or C#, new is used to create new objects, so you use it everywhere. in C++, it is only used for heap allocations. Almost all objects should be stack-allocated (or created in-place as class members) so that the language's scoping rules help us manage their lifetimes. new isn't often necessary. Usually, when we want to allocate new objects on the heap, you do it as part of a larger collection, in which case you should just push the object onto your STL container, and let it worry about allocating and deallocating memory. If you just need a single object, it can typically be created as a class member or a local variable, without using new.
  • Should new be present in your business logic code? Rarely, if ever. As mentioned above, it can and should be typically be hidden away inside wrapper classes. std::vector for example dynamically allocates the memory it needs. So the user of the vector doesn't have to care. I just create a vector on the stack, and it takes care of the heap allocations for me. When a vector or other container class isn't suitable, we may want to write our own RAII wrapper, which allocates some memory in the constructor with new, and releases it in the destructor. And that wrapper can then be stack-allocated, so the user of the class never has to call new.

One of the articles asserted that Foo *f = new Foo(); was nearly unacceptable professional C++ code by and large, and an automatic memory management solution was appropriate.

If they mean what I think they mean, then they are right. As I said above, new should usually be hidden away in wrapper classes, where automatic memory management (in the shape of scoped lifetime and objects having their destructors called when they go out of scope) can take care of it for you. The article doesn't say "never allocate anything on the heap" or never use new", but simply "When you do use new, don't just store a pointer to the allocated memory. Place it inside some kind of class that can take care of releasing it when it goes out of scope.

Rather than Foo *f = new Foo();, you should use one of these:

Scoped_Foo f; // just create a wrapper which *internally* allocates what it needs on the heap and frees it when it goes out of scope
shared_ptr<Foo> f = new Foo(); // if you *do* need to dynamically allocate an object, place the resulting pointer inside a smart pointer of some sort. Depending on circumstances, scoped_ptr, or auto_ptr may be preferable. Or in C++0x, unique_ptr
std::vector<Foo> v; v.push_back(Foo()); // place the object in a vector or another container, and let that worry about memory allocations.
jalf
  • 243,077
  • 51
  • 345
  • 550
2

I stopped writing such code some time ago. There are several alternatives:

Scope based deletion

{
    Foo foo;
    // done with foo, release
}

scoped_ptr for scope based dynamical allocation

{
    scoped_ptr<Foo> foo( new Foo() );
    // done with foo, release
}

shared_ptr for things that should be handled in many places

shared_ptr<Foo> foo;
{ 
    foo.reset( new Foo() );
} 
// still alive
shared_ptr<Foo> bar = foo; // pointer copy
...
foo.reset(); // Foo still lives via bar
bar.reset(); // released

Facory-based resource management

Foo* foo = fooFactory.build();
...
fooFactory.release( foo ); // or it will be 
                           // automatically released 
                           // on factory destruction
Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
1

In general, no, but the general case is not the common case. Which is why automatic schemes like RAII were invented in the first place.

From an answer I wrote to another question:

The job of a programmer is to express things elegantly in his language of choice.

C++ has very nice semantics for construction and destruction of objects on the stack. If a resource can be allocated for the duration of a scope block, then a good programmer will probably take that path of least resistance. The object's lifetime is delimited by braces which are probably already there anyway.

If there's no good way to put the object directly on the stack, maybe it can be put inside another object as a member. Now its lifetime is a little longer, but C++ still doe a lot automatically. The object's lifetime is delimited by a parent object — the problem has been delegated.

There might not be one parent, though. The next best thing is a sequence of adoptive parents. This is what auto_ptr is for. Still pretty good, because the programmer should know what particular parent is the owner. The object's lifetime is delimited by the lifetime of its sequence of owners. One step down the chain in determinism and per se elegance is shared_ptr: lifetime delimited by the union of a pool of owners.

> But maybe this resource isn't concurrent with any other object, set of objects, or control flow in the system. It's created upon some event happening and destroyed upon another event. Although there are a lot of tools for delimiting lifetimes by delegations and other lifetimes, they aren't sufficient for computing any arbitrary function. So the programmer might decide to write a function of several variables to determine whether an object is coming into existence or disappearing, and call new and delete.

Finally, writing functions can be hard. Maybe the rules governing the object would take too much time and memory to actually compute! And it might just be really hard to express them elegantly, getting back to my original point. So for that we have garbage collection: the object lifetime is delimited by when you want it and when you don't.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

First of all, i believe it should be Foo *f = new Foo();

And the reason I don't like using that syntax because it is easy to forget to add a delete at the end of the code and leave your memory a-leakin'.

zmbush
  • 2,790
  • 1
  • 17
  • 35
  • 2
    What other syntax would you use to accomplish the same thing? – Joe Phillips Jan 11 '10 at 06:11
  • Not exactly the same thing, but I usually use `Foo f = Foo();` – zmbush Jan 11 '10 at 06:14
  • Ooops. Forgot the parens. That's not what I was referring to. :) – Paul Nathan Jan 11 '10 at 06:15
  • Well, then I guess I misunderstood what you were asking. – zmbush Jan 11 '10 at 06:18
  • I was asking - "direct memory management or smart pointer for new, professional-quality C++ code" – Paul Nathan Jan 11 '10 at 06:20
  • 5
    `new Foo` is correct -- there is no need for parens if you are invoking the default constructor. (But yes, I would avoid the new/delete.) – Jon Reid Jan 11 '10 at 06:20
  • Your memory will a-leak even if you put that delete at the end of the code, as you will undoubtedly trip over an exception at some point, and the delete will just get skipped over -> Leakage Happens! (why auto_ptr is preferred over "just remember to add the delete") – PaulMcG Feb 11 '11 at 19:28
0

In general your example is not exception safe and therefore shouldn't be used. If the line directly following the new throws? The stack unwinds and you have just leaked memory. A smart pointer will take care of it for you as part of the stack unwind. If you tend to not handle exceptions then there is no draw back outside of RAII issues.

stonemetal
  • 6,111
  • 23
  • 25