3

I know the title of the question looks a bit mind-damaging, but I really don't know how to ask this in one phrase. I'll just show you what I mean:

void f(T *obj)
{
    // bla bla
}
void main()
{
    f(new T());
}

As far as I know, (almost) every new requires a delete, which requires a pointer (returned by new). In this case, the pointer returned by new isn't stored anywhere. So would this be a memory leak?

Does C++ work some kind of magic (invisible to the programmer) that deletes the object after the function ends or is this practice simply always a bad idea?

conectionist
  • 2,094
  • 2
  • 25
  • 43
  • It certainly isn't deleted after the function ends. It could be deleted *at the end of the function*, but in that case it's essentially a local variable and doesn't need to be heap allocated. – Matt Phillips Oct 25 '12 at 21:18
  • 2
    It'll leak unless `f()` deletes it. – Fred Larson Oct 25 '12 at 21:19
  • @FredLarson But that wouldn't be good programming practice, would it... – conectionist Oct 25 '12 at 21:21
  • 1
    @connectionist - that's basically how smart pointer constructors work - you `new()` something, and pass it to the constructor. The smart pointer is then responsible for deletion. But I agree, in general, it's a code smell. – Roddy Oct 25 '12 at 21:23

7 Answers7

7

There is no particular magic and delete will not be called automatically.

It is definitely not "always a bad idea" - if function takes ownership of an object in some form it is perfectly valid way for calling such function:

container.AddAndTakeOwnership(new MyItem(42));
Alexei Levenkov
  • 94,391
  • 12
  • 114
  • 159
  • @conectionist, yes. But ecatmur's answer is nicer with different meaning of "bad idea" (mine is "code is not outright wrong", by ecatmur's is "code is safe and self documenting") – Alexei Levenkov Oct 25 '12 at 21:37
5

Yes, it's always a bad idea; functions and constructors should be explicit about their ownership semantics and if they expect to take or share ownership of a passed pointer they should receive std::unique_ptr or std::shared_ptr respectively.

There are lots of legacy APIs which take raw pointers with ownership semantics, even in the standard library (e.g. the locale constructor taking a Facet * with ownership), but any new code should avoid this.

Even when constructing a unique_ptr or shared_ptr you can avoid using the new keyword, in the latter case using make_shared and in the former case writing a make_unique function template which should fairly soon be added to the language.

Community
  • 1
  • 1
ecatmur
  • 137,771
  • 23
  • 263
  • 343
4

The code shown will result in a memory leak. C++ does not have garbage collection unless you explicitly use a specialized framework to provide it.

The reason for this has to do with the way memory is managed in C/C++. For a local variable, like your example, memory for the object is requested directly from the operating system (malloc) and then the pointer to the object exists on the stack. Because C/C++ can do arbitrarily complex pointer arithmetic, the compiler has no way of knowing whether there exists some other pointer somewhere to the object, so it cannot reclaim the memory when function f() ends.

In order to prevent the leak automatically, the memory would have to be allocated out of a managed heap, and every reference into this heap would have to be carefully tracked to determine when a given object no longer was being used. You would have to give up C's ability to do pointer arithmatic in order to get this capability.

For example, let's say the compiler could magically figure out that all normal references to obj were defunct and deleted the object (released the memory). What if you had some insanely complicated RUNTIME DEPENDENT expression like void* ptr = (&&&&(&&&*obj)/2++ - currenttime() - 567 + 3^2 % 52) etc; How would the compiler know whether this ptr pointed to obj or not? There is no way to know. This is why there is no garbage collection. You can either have garbage collection OR complex runtime pointer arithmetic, not both.

Tyler Durden
  • 10,296
  • 8
  • 53
  • 109
  • C++11 defines semantics for *traceable pointer objects* (**3.7.4.3 [basic.stc.dynamic.safety]**) in order to work with precisely this scenario. Admittedly, implementations enforcing strict pointer safety are still fairly rare. – ecatmur Oct 25 '12 at 21:35
  • Or you can use modern C++ features that manage memory for you `std::shared_ptr` or `boost::ptr_vector` etc. Most of the above is totally irrelevant to modern C++. – Martin York Oct 25 '12 at 22:00
4

In this case, the pointer returned by new isn't stored anywhere. So would this be a memory leak?

No, it is not necessarily a memory leak. The pointer is stored as an argument to f:

void f(T *obj)
//        ^^^  here pointer is "stored"
{
    // bla bla
    delete obj; // no memory leak if delete will be called on obj
}
void main()
{
    f(new T());
  //  ^^^^^^^  this "value" will be stored as an argument to f
}

Does C++ work some kind of magic (invisible to the programmer) that deletes the object after the function ends or is this practice simply always a bad idea?

No magic in your example. As I showed - delete must be called explicitly.

Better is to use smart pointer, then C++ "magic" works, and delete is not needed.

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
void main()
{
    f(new T());
}
PiotrNycz
  • 20,687
  • 7
  • 55
  • 102
2

This is often used when your function f() is storing the object somewhere, like an array (or any other data container) or a simple class member; in which case, deletion will (and must) take place somewhere else.

Otherwise is not a good idea cause you will have to delete it manually at the end of the function anyway. In that case, you can just declare an automatic (on the stack) object and pass it by pointer.

imreal
  • 9,700
  • 2
  • 28
  • 45
1

No magic. In your case after f is called main returns back to CRT's main and eventually the OS will clean up the "leak". Its not necessarily a bad idea, it could be giving f the ownership and it is up to f to do stuff and eventually delete. Some make call it bad practice but its probably out there in the wild.

Edit: Although I see that code no more dangerous than:

void f(T *obj)
{
    // bla bla
}
void main()
{
    T* test = new T ();
    f(test);
}

Principally it is the same. Its saying to f, here is a pointer to some memory, its yours you look after it now.

Science_Fiction
  • 3,273
  • 17
  • 24
  • Being out there in the wild does not mean it's not bad practice. The only justification is passing ownership, in which case a modern code base should use `unique_ptr`, which provides the exact behavior that is described. In an older codebase (pre-C++11), the idea of ownership must be documented to help to avoid double freeing the memory. – pickypg Oct 25 '12 at 21:26
  • I know it is... I've seen this (in a QRegExpValidator example, in a book, if I'm not mistaken) and it looked fishy... – conectionist Oct 25 '12 at 21:30
  • @pickypg I'm not advising it. Just saying someone, somewhere out there will have a reason. They always do and its not always a bad reason. – Science_Fiction Oct 25 '12 at 21:32
1

In C++ passing a pointer is discouraged as there are no associated owner semantics (ie you can not know who owns the pointer and thus who is responsible for deleting the pointer). Thus when you do have a function (that takes a pointer) you need to document very clearly if the function is responsible for cleaning up the pointer or not. Of course documentation like this is very error prone as the user has to read it.

It is more normal in C++ program to pass objects that describe the ownership semantics of the thing.

  1. Pass by reference
    The function is not taking ownership of the object. It will just use the object.
  2. Pass a std::auto_ptr (or std::unique_ptr)
    The function is being passed the pointer and ownership of the pointer.
  3. Pass a std::shared_ptr
    The function is being passed shared ownership of the pointer.

By using these techniques you not only document the ownership semantics but the objects used will also automatically control the lifespan of the object (thus relieving your function from calling delete).

As a result it is actually very rare to see a manual call to delete in modern C++ code.

So I would have written it like this:

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
int main()
{
    f(std::unique_ptr<T>(new T()));
}
Martin York
  • 234,851
  • 74
  • 306
  • 532