1

Here is the code:

class A {
    private:
        int *anArr;
        int id;
    public:
        A() {
            id = 0;
            anArr = new int[10];
        }
        A(int i) {
            id = i;
            anArr = new int[10];
        }
        ~A() {
            delete[] anArr;
            std::cout << "Class A id : " << id << " destructor" << std::endl;
        }
    };

    class B {
    private:
        A *anArr;
    public:
        B() {
            anArr = new A[10];
        }
        ~B() {
            std::cout << "Class B destructor" << std::endl;
            delete[] anArr;
        }
        void changeAnElement() {
            anArr[2] = A(1);
            anArr[2] = A(2);
        }
    };

    int main()
    {
        B b;
        b.changeAnElement();

        return 0;
    }

Output:

Class A id : 1 destructor
Class A id : 2 destructor
Class B destructor
Class A id : 0 destructor
Class A id : 0 destructor
Class A id : 0 destructor
Class A id : 0 destructor
Class A id : 0 destructor
Class A id : 0 destructor
Class A id : 0 destructor
// Gives heap error here

So if I'm not wrong, when I change an element of object array it does not call destructor. My first question is what happens to old object at the changed index? Does the array in it leak? I thought that I need to call destructor myself to prevent memory leak but it gives heap error. Second question is I'm getting heap error (Expression: _CrtlsValidHeapPointer(block)) when destructor of the changed object called. I have no idea why, it works fine for the ones created in constructor. Thanks!

  • 1
    Do note that if you are doing manual memory management in the class you need to follow the rule of 3 or 5. – NathanOliver Nov 29 '16 at 16:48
  • 1
    I would imagine that `anArr[0] = A(1)` is equivalent to `anArr[0].operator=(A(1))`, which would construct a new `A` with `id=1`, call the copy constructor of `anArr[0]` with that `A, id=1`, then destruct `A, id=1`. The default implementation for `A::operator=` isn't going to manage your memory for you, so you leak. Solution: Implement the copy constructor or use `std::vector`/`std::array` – lcs Nov 29 '16 at 16:51
  • @NathanOliver thanks, i've never heard of them. – Halil İbrahim Azak Nov 29 '16 at 17:05
  • @Ics thanks, we are not allowed to use vector – Halil İbrahim Azak Nov 29 '16 at 17:05
  • @HalilİbrahimAzak if you haven't heard of them, then I would recommend you to study how manual dynamic memory allocation should be done, instead of trying to do it without knowledge, only to fall into the pitfalls. This might help: http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – eerorika Nov 29 '16 at 17:08
  • @user2079303 i will, thanks for guidance – Halil İbrahim Azak Nov 29 '16 at 17:19

2 Answers2

3

My first question is what happens to old object at the changed index?

An object in an array never goes anywhere. The "old" object remains in that index. You invoke the assignment operator on that object. The assignment operator modifies the object.

Does the array in it leak?

The array that the object pointed to before the assignment does leak, yes.

I thought that I need to call destructor myself to prevent memory leak

You created the object with new[], so you do need to call delete[], which indeed calls the destructors.

but it gives heap error

That's because you forgot to follow the rule of 3 (or of 5).

anArr[2] contains the same pointer that the temporary A(2) contained, but since the destructor of the temporary has already run, it has already deleted the array and the destructor of anArr[2] then tries to delete it again. Which is one of the things that must not be done.


Conclusions:

  • When you do manual memory management, follow the rule of 3
  • Don't do manual memory management. Use std::vector or std::array here instead.
eerorika
  • 232,697
  • 12
  • 197
  • 326
1

What happens to the old object at the changed index?

It is re-assigned. In C++, this line

anArr[2] = A(1);

makes a new temporary object A(1), assigns that value into the existing object anArr[2], and destroys the temporary object. anArr[2] is the same object throughout, only its value changes. Since it is not newly created, it is also not destroyed at this point. But notice that the temporary object was destroyed, and deleted that brand new int[10] that anArr[2] thinks (mistakenly) that it owns.

When the value is a pointer to existing resources that need to be freed, you need to write a user-defined assignment operator, A::operator=(const A&). The "Rule of Three" says that most cases where you need a custom destructor, custom copy constructor, or custom copy assignment operator, you also need both the other two. (Since C++11, move constructor and move assignment are added to that list, making the "Rule of Five").

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720