0

We made a dynamic array class during lecture and the instructor copy-pasted the copy_constructor code on the assignment_operator_overload. But don't we need to delete the existing dynamic array first for the assignment operator ? (Please refer void "operator=(DynamicArray const &d){}" function at the 29th line)

#include <iostream>
using namespace std;

class DynamicArray {
    int *data;
    int nextIndex;
    int capacity;           // total size

    public :

    DynamicArray() {
        data = new int[5];
        nextIndex = 0;
        capacity = 5;
    }

    DynamicArray(DynamicArray const &d) {
        //this -> data = d.data;        // Shallow copy
    
        // Deep copy
        this -> data = new int[d.capacity];
        for(int i = 0; i < d.nextIndex; i++) {
            this -> data[i] = d.data[i];
        }
        this -> nextIndex = d.nextIndex;
        this -> capacity = d.capacity;
    }

    void operator=(DynamicArray const &d) {
        // I think here we should add // delete []this->data;
        this -> data = new int[d.capacity];
        for(int i = 0; i < d.nextIndex; i++) {
            this -> data[i] = d.data[i];
        }
        this -> nextIndex = d.nextIndex;
        this -> capacity = d.capacity;
    }


    void add(int element) {
        if(nextIndex == capacity) {
            int *newData = new int[2 * capacity];
            for(int i = 0; i < capacity; i++) {
                newData[i] = data[i];
            }
            delete [] data;
            data = newData;
            capacity *= 2;
        }
        data[nextIndex] = element;
        nextIndex++;
    }

    //Here add can only modify the previous, present and just_next index.
    void add(int i, int element) {
            if(i < nextIndex) {
                data[i] = element;
            }
            else if(i == nextIndex) {
                add(element);
            }
            else {
                return;
            }
    }

};


int main() {
    DynamicArray d1;

    d1.add(10);
    d1.add(20);
    d1.add(30);
    d1.add(40);
    d1.add(50);
    d1.add(60);

    d1.add(9, 100);
    d1.print();

    DynamicArray d2(d1);        // Copy constructor

    DynamicArray d3 = d1;      // Copy constructor

    d3 = d1;                   // Assignment operator
}
3
  • // I think here we should add // delete []this->data; You may be right, you need to de-allocate the memory previous data, but this is only when capacity is different, if it's same then you can just copy the new data. Commented Feb 5, 2022 at 6:11
  • The implementation of void add(int i, int element) is incorrect btw, or at the very least the contract is weird. I'd expect this to do an insertion of an element at index i, but in case you specify the index of an existing element, you simply overwrite the element. For insertion you'd first need to allocate a new array, if necessary, and move the elements with a index >= the insertion index back by 1 and only then write the element. Commented Feb 5, 2022 at 8:19
  • In the assignment operator overload you should check for if(this != &other) so that you don't lose data on self assignment. Commented Feb 5, 2022 at 8:39

2 Answers 2

0

Right, you do. But before that you need to check for the self assignment before deleting. A step forward would be to use copy and swap idiom. Its more clean, less error prone and gives better exception safety guaranties.

Sign up to request clarification or add additional context in comments.

2 Comments

Can you edit and post a code for copy and swap idiom? Also, can you include a brief explanation for copy and swap idion?
@kiner_shah I included a link which explains it in depth
0

Indeed you're right. In the implementation of the class little care was taken for preventing a memory leak as clearly indicated by the lack of an destructor. As for deleting the old array first: you may not want to do this, if you want a strong exception guarantee, since new int[] could throw an exception and a strong exception guarantee requires the old data to still be available, if the memory allocation throws.

void operator=(DynamicArray const &d) {
    
    int* newData = new int[d.capacity]; // may throw; don't delete old data yet
    delete[] data;// the rest of the function doesn't throw -> free the memory now
    data = newData;

    for(int i = 0; i < d.nextIndex; i++) {
        data[i] = d.data[i];
    }
    nextIndex = d.nextIndex;
    capacity = d.capacity;
}

// also need to free the memory at the end of the object lifetime
~DynamicArray()
{
    delete[] data;
}

You may btw want to use std::unique_ptr to take care of managing the memory for you; this gives you a strong exception guarantee without the need of explicitly delete and writing a destructor. Furthermore you could simply default move constructor&move assignment operators this way:

class DynamicArray {
    std::unique_ptr<int[]> data;
    size_t nextIndex;
    size_t capacity;           // total size

public:
    DynamicArray(size_t initialCapacity = 5) // we can allow the user to optionally specify the initial capacity with little effort
        : data(std::make_unique<int[]>(initialCapacity)),
        nextIndex(0),
        capacity(initialCapacity)
    {
    }

    DynamicArray(DynamicArray const &d)
        : data(std::make_unique<int[]>(d.capacity)),
        nextIndex(d.nextIndex),
        capacity(d.capacity)
    {
        std::copy_n(d.data.get(), nextIndex, data.get()); // we can use the algorithms lib to write the loop as a single function call
    }

    DynamicArray& operator=(DynamicArray const &d) { // standard signature returns a reference to self
        data = std::make_unique<int[]>(d.capacity);
        capacity = d.capacity;
        nextIndex = d.nextIndex;
        std::copy_n(d.data.get(), nextIndex, data.get());
        return *this;
    }

    // we get the following 2 "for free", since all member variables are move constructible/assignable
    DynamicArray(DynamicArray&&) = default;
    DynamicArray& operator=(DynamicArray&&) = default;

    void add(int element) {
        ...
    }

    //Here add can only modify the previous, present and just_next index.
    void add(size_t i, int element) {
        ...
    }
};

3 Comments

Can you explain - " new int[] could throw an exception" ?
Can you give an example where "new int[] could throw an exception" would throw an exception
@VoidRust It's very rare nowadays, since we usually have a large amount of main memory available, but if the capacities get really high and at some point you aren't able to allocate a sufficient amount of memory, new[] fails throwing a std::bad_alloc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.