4

P.S: I am new to programming so please answer my doubts in simpler terms. I have found a couple of answers but could not understand them. Below is the copy constructor and assignment operator overload.

template <class T>
Mystack<T>::Mystack(const Mystack<T> &source)            // copy constructor
{
    input = new T[source.capacity];
    top = source.top;
    capacity = source.capacity;
    for (int i = 0; i <= source.top; i++)
    {
        input[i] = source.input[i];
    }
}


template <class T>
Mystack<T> & Mystack<T>::operator=(const Mystack<T> &source)       // assignment operator overload 
{
    input = new T[source.capacity];
    top = source.top;
    capacity = source.capacity;

    for (int i = 0; i <= source.top; i++)
    {
        input[i] = source.input[i];
    }
    return *this;
}

main function snippet

   Mystack <int> intstack = tempstack; (copy constructor)
    Mystack <int> floatstack, temp_1;
    floatstack = temp_1;  (assignment operator)

Understanding : I understand that we need copy and assignment operator so that we can have deep copying in case if we are using heap memory and thus there will be no dangling pointer issue when we delete one of the objects.

Can some one please answer below questions.

1. : Is my understanding correct?

2. : I have been suggested by developers that I have memory leak in assignment operator. If yes, can some please explain me how?

3. Copy constructor has more or less same code as assignment operator then how come I have memory leak only in case of assignment operator but not in the copy constructor function.

4. : In case I really have memory leak. What magic copy and swap idiom does that mem leak gets resolved.

P.S: Its not the complete running code. In actual code objects does contain some data. Kindly bear!

tanz
  • 627
  • 1
  • 10
  • 21
  • 2
    For the assignment operator, what if the object is already containing data? What happens with that data? – Some programmer dude Jan 11 '15 at 19:18
  • 1
    i.e. where do you think the *old* `input ` went in your target object? – WhozCraig Jan 11 '15 at 19:21
  • And once you handle disposing of the old buffer, ask yourself — what happens if assignment operator was called on the object itself, i.e. foo = foo? Thats when you may need some magic (i.e. item 4 on your list). – Nick Zavaritsky Jan 11 '15 at 19:26
  • For an in-depth explanation of the copy/swap idiom and the problems it addresses, [**read this**](http://stackoverflow.com/a/3279550/1322972) – WhozCraig Jan 11 '15 at 19:27
  • @JoachimPileborg : You mean to say what if `floatstack` already had some data? – tanz Jan 11 '15 at 19:30
  • @JoachimPileborg : Correct me If I am wrong. When I declare `floatstack` constructor gets called and thus memory gets allocated. And when I call assignment operator I need to get rid of old memory that's been allocated. – tanz Jan 11 '15 at 19:41
  • 1
    Again, yes that's right. :) – Some programmer dude Jan 11 '15 at 20:06
  • 1
    Unless this is solely an exercise to learn about how to write copy-constructors etc., consider following the Rule of Zero and have your class contain a vector instead of a manually-managed pointer. – M.M Jan 12 '15 at 00:15
  • If the OP is still watching this, I can't stress the importance of Matt's comment enough. – WhozCraig Jan 12 '15 at 00:17

2 Answers2

8

"Is my understanding correct?"

Yes you seem to understand. The full reasons are best described by the Rule of Three concept. If you find yourself having to implement any of the three (copy-ctor, assignment op, or destructor) to manage dynamic memory, you very likely need all three (and maybe more, see the article).


"I have been suggested by developers that I have memory leak in assignment operator. If yes, can some please explain me how?"

You've not posted your default constructor, but I assume it looks something like this:

Mystack<T>::Mystack(size_t size = N)
{
    input = new T[size];
    top = 0;
    capacity = size;
}

or something similar. Now, Lets see what happens in your assignment operator:

input = new T[source.capacity]; 

Um, what just happened to the old value in input for this object? It is no longer reachable and with it the memory therein is no longer reclaimable. It is leaked.


"Copy constructor has more or less same code as assignment operator then how come I have memory leak only in case of assignment operator but not in the copy constructor function."

There is no previous value for input assigned in the target of the copy construction in your copy-ctor. I.e. input doesn't point to anything yet (how could it? you're just-now creating the target object). Thus, no leak.


"In case I really have memory leak. What magic copy and swap idiom does that mem leak gets resolved."

The copy-swap idiom uses the copy-constructor to create a temporarily-held value-copy, then uses the assignment operator to swap object "guts" with that copy. in doing so, the out-going temporary will destroy the target object's original content when its destructor fires, while the target object has taken ownership of the temporary's incoming content as its own.

This provides multiple benefits (and yes, one drawback), and is brilliantly described here. A simple example in your code is:

template <class T>
void Mystack<T>::swap(Mystack<T>& src)
{
    std::swap(input, src.input);
    std::swap(top, src.top);
    std::swap(capacity, src.capacity);
}

and your assignment operator becomes:

template <class T>
Mystack<T> & Mystack<T>::operator=(Mystack<T> src) // NOTE by-value intentional,
                                                   // invokes copy-ctor.
{
    this->swap(src);
    return *this;
}

Now you have one implementation of copying (in the copy-ctor) to manage. Further, if any exceptions happen they will do so during the construction of the value-copy, not here. The chances of this object being polluted to indeterminate state are reduced (a good thing)

If you're curious of the drawback I mentioned earlier, consider how self-assignment (x = x;) would play out with a paradigm such as this. Honestly, I personally don't consider self-assignment inefficiency a drawback. If your code frequents the likes of x = x; you probably have a putrid smell in your design to begin with.


I strongly suggest you read the article for other info on the concept. It is one of those may-change-how-you-think things that you'll remember the rest of your career.

Community
  • 1
  • 1
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • The answer describing copy-and-swap suggests `using std::swap; swap(...)` instead of `std::swap(...)` – anatolyg Jan 11 '15 at 23:32
  • @anatolyg correct. And the comment adjacent specifies why (ADL). The OP has enough on his plate without having to add [Koenig](http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism) to the morass. Like that example, it isn't a problem here either. – WhozCraig Jan 11 '15 at 23:36
1

The purpose of the copy constructor is so that the input pointer of two instances of the class do not end up pointing to the same buffer in the heap. If they do, modifying one stack will affect the other and the destructor of one will free the memory of the other, resulting in use-after-free errors.

Your assignment operator does result in a memory leak because it does not free the memory that was previously allocated to that instance of the stack. Therefore, the buffer that input was pointing to does not end up being de-allocated when the destructer was called. This is not a problem with the copy constructor because it is only called on a new instance of the class, which does not have any memory allocated to it from before. In order to solve the problem, add the following line to the beginning of the assignment operator:

delete [] input;
Arthur Laks
  • 524
  • 4
  • 8
  • 3
    Note: doing this can leave your object in indeterminate state if an exception can be thrown between the time `delete[] input` is called and the time `input = new[...];` *completes*. (such as if the `new` *fails*). Addressing that issue is one of several goals of copy-swap-idiom. And self-assignment will obviously puke (`x = x;`). – WhozCraig Jan 11 '15 at 19:30
  • @Arthur Laks: So you mean to say what if my floatstack already had some data before calling assignment operator? – tanz Jan 11 '15 at 19:36
  • 1
    @tanz The stack almost definitely had data before calling the assignment operator. If it was not previously initialized then the copy constructor would have been called instead. – Arthur Laks Jan 12 '15 at 01:34
  • ya, figured it out.. :) Thanks – tanz Jan 12 '15 at 01:56