0

In the following code, there is a memory leak if Info::addPart1() is called multiple times by accident:

typedef struct
{
}part1;

typedef struct
{
}part2;

class Info
{
    private:
    part1* _ptr1;
    part2* _ptr2;    

    public:
    Info()
    {
      _ptr1 = _ptr2 = NULL;
    }

    ~Info()
    {
      delete _ptr1; 
      delete _ptr2;
    }

    addPart1()
    {
       _ptr1 = new part1;         
    }

    addPart2()
    {
      _ptr2 = new part2;         
    }   
};


Info _wrapper;
_wrapper.addPart1();
_wrapper.addPart2();

Is there a C++ idiom to handle this problem ?

I could rewrite addPart1 and addPart2 like this to defend the MLK

addPart1()
{
  if(_ptr1 != NULL) delete _ptr1;
  _ptr1 = new part1;         
}

Is that a good solution?

jalf
  • 243,077
  • 51
  • 345
  • 550
Vijay Angelo
  • 766
  • 6
  • 14
  • tried to make your question a bit more readable. I still have no clue what you mean by MLK though, but at least, now readers don't have to scroll down to the bottom of the code to see what the question is. :) – jalf Jun 06 '09 at 13:30
  • On a side note, you should avoid names with leading underscores. Names that begin with an underscore, followed by a lower-case letter are reserved in the global namespace. That means they're technically legal as class members, but it's a bit too close for comfort. (depending on where the _wrapper variable occurs, it might be illegal there) An easy fix is to put the underscore at the end of the name instead of at the front. – jalf Jun 06 '09 at 13:32
  • Names and underscores: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797 – Martin York Jun 06 '09 at 15:25
  • You also need to worry about the compiler generated version of assignment and copy constructor. see http://stackoverflow.com/questions/214891/checklist-for-writing-copy-constuctor-and-assignment-operator-in-c/215210 – Martin York Jun 06 '09 at 15:32
  • @jalf - MLK is Purify's code for Memory Leak. – SCFrench Jun 07 '09 at 01:27

11 Answers11

10

Use a smart pointer such as boost:shared_ptr , boost:scoped_ptr is recommended to manage the raw pointer. auto_ptr is tricky to work with, you need pay attention to that.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
J.W.
  • 17,991
  • 7
  • 43
  • 76
  • The tricky part is copying, where the ownership of the pointer is passed to the new copy of auto_ptr. – Miroslav Bajtoš Jun 06 '09 at 12:59
  • 1
    tricky perhaps, but auto_ptr is designed with that in mind. when passing the managed pointer to a new owner, pass(or assign) as another auto_ptr. when using it for some purpose not related to passing ownership, use the get() method. – SingleNegationElimination Jun 06 '09 at 13:14
  • TokenMacGuy ,I agree with you and I think it's just personal reference. I find scoped_ptr and shared_ptr are enough for my needs, so I try to avoid remember all the details about auto_ptr. – J.W. Jun 06 '09 at 13:39
  • Once you have a choice between scoped_ptr and auto_ptr, auto_ptr is nice for documenting the fact that the code involves ownership transfers. I'm particularly partial to returning auto_ptr from factory methods; it draws attention to the fact that this is something the caller needs to take responsibility for. If you just return a naked pointer, you have to find some other way of documenting ownership issues (e.g naming conventions). Of course historically (pre-boost), auto_ptr was all there was so it got used as a scoped_ptr too. – timday Jun 06 '09 at 13:58
  • @Timday: Wrong... See loki-lib :) – the_drow Jun 07 '09 at 04:46
5

You should read about the smart pointer idiom and about RAII. I suggest taking a look into the new technical report (TR1).
Take a good look here and here.
Also take a look at boost's smart pointers.
I recommend loki-lib's SmartPtr or StrongPtr classes.

the_drow
  • 18,571
  • 25
  • 126
  • 193
5

Bear with me here...

In the distant past, programmers used constructs like "jump" and "goto" for flow control. Eventually common patterns emerged and constructs like for, do/while, function call and try/catch emerged, and the spaghetti was tamed. Those named constructs give a lot more information about intent than a generic goto, where you have to examine the rest of the code for context to understand what it's doing. In the unlikely event you see a goto in modern code by a competent coder, you know something pretty unusual is going on.

In my opinion, "delete" is the "goto" of memory management. There are enough smart pointer and container classes available to the modern developer that there's very little reason for most code to contain a single explicit delete (other than in the smart pointer implementations of course). When you see a plain "delete" you get no information about intent; when you see a scoped_ptr/auto_ptr/shared_ptr/ptr_container you get a lot more.

ie the idiom should be to aspire to write delete-free code by using appropriate smart pointer types (as recommended by just about every other answer here).

Update 2013-01-27: I note Herb Sutter's excellent talk on C++11 includes some similar sentiments re delete free code.

timday
  • 24,582
  • 12
  • 83
  • 135
4

Checking for nonzero pointer before delete is redundant. delete 0 is guaranteed to be a no-op.

A common way to handle this is

delete _ptr1;
_ptr1 = 0;
_ptr1 = new part1;

Zeroing the pointer ensures there won't be any dangling pointers for example in the case part1 construction throws an exception.

laalto
  • 150,114
  • 66
  • 286
  • 303
  • This doesn't solve the problem of avoiding memory leaks though. The correct answer is to use some form of RAII. Either rewriting the part1/part2 classes are RAII objects, or wrapping the pointers in smart pointers of some form, as suggested by others. – jalf Jun 06 '09 at 13:25
  • @jalf: Care to elaborate about the leak avoidance? In this case _ptr1 and _ptr2 are owned by the Info object and deleted in the destructor. No leaks there. – laalto Jun 06 '09 at 13:37
  • But why is it the Info object's responsibility to take care of memory allocations related to part1 and part2? It doesn't scale. What happens in the next class that needs to allocate a part1 object? What happens when Info gets more complex, and has to juggle parts 1-10, rather than just 1 and 2? If multiple methods might be allowed to update these pointers? Sooner or later, you're going to forget a delete, and then you have a leak. Wrapping the pointer in a smart pointer means that you don't need to remember to call delete in the first place. – jalf Jun 06 '09 at 13:52
  • @laalto: Can you refer me to the section in the standard that claims that deleting null is a no-op? I believe it throws a std::bad_alloc exception. – the_drow Jun 06 '09 at 14:02
  • 1
    @the_drow: http://www.open-std.org/jtc1/sc22/open/n2356/expr.html#expr.delete "if the value of the operand of delete is the null pointer the operation has no effect" – laalto Jun 06 '09 at 14:42
  • 1
    Never delete before you create the new object. If the exception is thrown then your object is now in an inconsistent state. Create into a temporary variable then swap. If the create worked (ie no exceptions) then the swap will work and delete should work. If the create fails (ie Exception) your object has not changed state. – Martin York Jun 06 '09 at 15:36
3

Your suggested fix will work (though of course you're still at risk for a memory leak if addPart2() is called twice). A much safer approach is to use scoped_ptr from the Boost library collection (www.boost.org), which is a container that acts like a pointer, but guarantees that its target is deleted when the container is destroyed. Your revised class would then look like

class Info
{
    private:
    boost::scoped_ptr<part1> _ptr1;
    boost::scoped_ptr<part2> _ptr2;    

    public:
    Info() {}  // scoped_ptrs default to null

    // You no longer need an explicit destructor- the implicit destructor
    // works because the scoped_ptr destructor handles deletion

    addPart1()
    {
      _ptr1.reset(new part1);
    }

    addPart2()
    {
      _ptr2.reset(new part2);         
    }   
};

As a general principle, it's a good idea to avoid writing code that requires you to explicitly delete pointers. Instead, try to use containers that do it automatically at the appropriate time. Boost is a good resource for this kind of thing.

All this assumes you have a reason ptr1_ and ptr2_ need to be pointers. If not, it's much better to make them ordinary objects; then you get memory management for free.

Geoff Romer
  • 2,358
  • 1
  • 18
  • 19
2

Use construction is initialization instead.

class Info
{
    private:
    part1* _ptr1;
    part2* _ptr2;    

    public:
    Info() : _ptr1(new part1), _ptr2(new part2)
    {
    }

    ~Info()
    {
      delete _ptr1; 
      delete _ptr2;
    }
};

But in this case you might as well create the parts on the stack, so no new and delete is required.

class Info
{
    private:
    part1 _part1;
    part2 _part2;    

    public:
    Info()
    {
    }

    ~Info()
    {
    }
};

But I guess you want the pointers to be lazy created, then I wouldn't suggest to create public class methods that takes care of the initializations. This should be handled internally in the class, when the class need to allocate them.

rtn
  • 127,556
  • 20
  • 111
  • 121
1

If you want it to have a lazy behavior you might consider this:

addPart1()
{
    if(_ptr1 == NULL) {
        _ptr1 = new part1;
    }
}

The way you suggested is also an alternative depending how you want it to behave. But other people have suggested better ways to do it, but as we really don't know why you made it this way and how the surrounding code works ...

Joakim Elofsson
  • 36,326
  • 1
  • 22
  • 28
1

I agree with the group that you should use some kind of smart pointer.

If you do decide to continue with bare pointers, be aware that your class above does not have a copy constructor defined by you. Therefore, the C++ compiler has defined one for you that will just do a simple copy of all the pointers; which will lead to a double delete. You'll need to define your own copy constructor (or at least create a stub private copy constructor if you don't think you need a copy constructor).

Info(const Info &rhs)
{
  _ptr1 = new part1[rhs._ptr1];
  _ptr2 = new part2[rhs._ptr2];
}

You will have a similar problem with the default assignment operator.

If you choose the correct smart pointer, these problems will go away. :)

David Coufal
  • 6,021
  • 5
  • 28
  • 30
0

Option 1: Use Java :)

Option 2: Use auto_ptr

std::auto_ptr<part1> _ptr1;
std::auto_ptr<part2> _ptr2;

public:
addPart1()
{
   _ptr1 = auto_ptr<part1>(new part1);
}

...

// no destructor is needed
Itay Maman
  • 30,277
  • 10
  • 88
  • 118
  • 4
    Using java isn't a solution. He needs to learn how to deal with memory if he's a C++ programmer. – the_drow Jun 06 '09 at 13:14
  • @the_drow: you're correct, but I think it was just a joke (note the ":)"). – Zifre Jun 06 '09 at 13:19
  • Well, for that reason I didn't downvote. But he should still be aware that Java just doesn't cut it for the most part :) – the_drow Jun 06 '09 at 13:21
0

You should take a look at RAII

Silfverstrom
  • 28,292
  • 6
  • 45
  • 57
0

On the far extreme of possible ways to deal with memory leaks is the boehm garbage collector, a conservative mark & sweep collector. Interestingly, this can be used in addition to all the good advice offered in other answers.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304