views:

392

answers:

7

I'm 99% certain the answer to this is a blinding no. Please validate my proposition that the following code will produce a memory leak.

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    Data d1 = getData();
    Data d2;

    /* d1 is not deallocated because it is on the heap, and d2 is
     * because it is on the stack. */
}

Please note that this is an oversimplified example, so clearly you wouldn't actually use the above code... So no need to point this out thanks.

Update 1:

To add to this, what if I assign the pointer to a reference? In this case, I assume that the data is not copied...

Data &getData()
{
    Data *i = new Data();
    return *i;
}

void exampleFunc()
{
    // Does copying occur here?
    Data &d1 = getData();

    // Does this deallocate the memory assigned to the pointer?
    delete &d;
}

Update 2:

I guess to answer my own question (in update 1) the following code proves that assigning a reference to a reference does not cause a copy...

#include <iostream>
#include <string>

using namespace std;

class Data
{
public:
    string mName;

    Data(const string &name) : mName(name)
    { cout << mName << " default ctor" << endl; }

    Data(const Data& other)
    {
        mName = other.mName + " (copy)";
        cout << mName << " copy ctor" << endl;
    }

    ~Data()
    { cout << mName << " dtor" << endl; }

    static Data &getData(const string &name)
    {
        Data *d = new Data(name);
        return *d;
    }
};

int main()
{
    cout << "d1..." << endl;
    Data d1 = Data::getData("d1");

    cout << "d2..." << endl;
    Data d2("d2");

    cout << "d3..." << endl;
    Data &d3 = Data::getData("d3");

    cout << "return..."  << endl;
    return 0;
}

Yields the following result...

d1...
d1 default ctor
d1 (copy) copy ctor
d2...
d2 default ctor
d3...
d3 default ctor
return...
d2 dtor
d1 (copy) dtor

Thanks to Eric Melski for a great answer (my code in update 2 is a modified copy of his exmaple code).

+7  A: 

d1 is a stack object, copy-constructed from the reference returned from getData(). d1 is freed, but the object created in getData() is leaked.

Chris Jester-Young
When you say copy-constructed. What happens if there is no constructor on Data that takes Data? Does a deep copy occur?
nbolton
No, a shallow copy occurs. i.e., any references and pointers in the object will point to the same objects as the original. _However_, the original object (as created in getData()) is still leaked, in this case.
Chris Jester-Young
+3  A: 

d1 and d2 are both stack object so will be destroyed at the end of their scope, the problem is that getData() creates a new heap object which is never deleted. d1 is copy-initialized from a reference to this heap object and d1 itself will be correctly destroyed at the end of exampleFunc, but the heap object generated in each call to getData() will not be deleted.

With the signature that getData() has it is possible to delete this object, but returning a reference to something that needs deleted is not an idiomatic interface.

Works, but is not a good interface:

Data& d = getData();
delete &d;
Charles Bailey
+1  A: 

You are correct in this. You have to explicitly deallocate memory using delete or delete[].

Tony k
+3  A: 

The only time when you'll regain your memory is when the OS reclaims it automatically upon program termination. During your program execution what you posted will cause a memory leak as C++ doesn't have a built-in garbage collector and requires manual memory management for heap allocated objects.

emaster70
+20  A: 

Actually both d1 and d2 will be deallocated, because they are both on the stack. What is not deallocated is the Data object you allocated in your getData() function. You can see this a little more clearly if you flesh out your Data class with instrumentation in the constructors and destructor. For example:

class Data {
public:
    Data() { cout << "Data default ctor" << endl; }
    Data(const Data& other) { cout << "Data copy ctor" << endl; }
    ~Data() { cout << "Data dtor" << endl; }

    static Data& getData()
    {
        Data *i = new Data();
        return *i;
    }
};

Note that I have explicitly declared the copy constructor for Data. In your example you are implicitly calling that constructor when you do Data d1 = getData();, and I suspect this is where your confusion is coming from. Now if I run this simple program:

#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
    Data d1 = Data::getData();
    Data d2;

    return 0;
}

The output is as follows:

Data default ctor
Data copy ctor
Data default ctor
Data dtor
Data dtor

Line-by-line, here's what you're seeing:

  1. The default constructor is invoked when you allocate a new Data in getData().
  2. The copy constructor is invoked to create d1 from the dynamically allocated Data you just made.
  3. The default constructor is invoked to create d2.
  4. The destructor is invoked for d2 when it goes out of scope at the end of main().
  5. The destructor is invoked for d1 when it goes out of scope at the end of main().

Note that there are three constructor calls, but only two destructor calls -- indicating that you have leaked exactly one Data object.

Hope that helps,

Eric Melski

Eric Melski
Very thorough. Nice.
Furious Coder
A: 

To be accurate, the answer of your question is yes.

In this code:

int main()
{
Data* d = new Data();
return 0; //end of execution
}

the data pointed-to by d is automatically freed by the operating system when the execution ends.

In other words, all the data which got allocated by program that finished executing but didn't get de-allocated by it (the program), will get de-allocated by the OS after (not during) the execution.

Lawand
+1  A: 

That is typical "way of thining" of Java/C# programmers.

In C++ you may actually use value types much more than you think:

Data getData()
{
    Data i;
    return i;
}

void exampleFunc()
{
    Data d1 = getData();
    /* d1 is constructed here and destroyed */
    Data d2;

}

If you want to return pointer, for example when you have some derived class that just use smart pointer like auto_ptr that allows movement of ownership:

auto_ptr<Data> getData()
{
    auto_ptr<Data> i(new Data());
    return i;
}

void exampleFunc()
{
    auto_ptr<Data> d1 = getData();
    /* now d1 is destroyed when goes out of scope */
    Data d2;
}
Artyom