'Copy Constructor vs Assignment Operator Overload in the dynamic array class in C++
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
}
Solution 1:[1]
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.
Solution 2:[2]
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) {
...
}
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Eduard Rostomyan |
| Solution 2 |
