Reputation: 85
I am currently working on a Vector class. I am required to use certain concepts such as templates, etc. For the most part I have completed the entire project, except there is a memory leak in which I'm unable to locate.
I'm using macOS Catalina and I've tried to install Valgrind however I cannot seem to get it to work. That is another issue in and of itself. Where is the memory leak located? And what is an easy way to detect where memory leaks are for macOS Catalina users?
I'm also using VS Code.
HEADER FILE
Note: ContainerIfc is an abstract class, all methods are implemented below that are needed to understand.
#ifndef PROJ7_MYVECTOR
#define PROJ7_MYVECTOR
#include "proj7-ContainerIfc.h"
template <class T>
class MyVector : public ContainerIfc<T>
{
public:
/**
* MyVector
*
* This is the default constructor that sets size equal
* to 0 and capacity to 10.
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector();
/**
* ~MyVector
*
* This is the destructor that deletes memory
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
~MyVector();
/**
* MyVector
*
* This is the copy constructor
*
* Parameters:
* v: the object that you want to copy over
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector(const MyVector &);
/**
* = operator
*
* This is the overloaded assignment operator
*
* Parameters:
* v: the object that you want to copy over
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector<T> &operator=(const MyVector &);
/**
* pushFront
*
* Prepends a value to the array
*
* Parameters:
* e: The value that you want to prepend
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector<T> &pushFront(T);
/**
* pushBack
*
* Appends a vlue to the array
*
* Parameters:
* e: The value that you want to append
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector<T> &pushBack(T);
/**
* popFront
*
* Removes the first index of the array and shifts all elements leftward
*
* Parameters:
* e: The value that was removed
*
* Output:
* return: none
* reference parameters: e
* stream: none
*/
MyVector<T> &popFront(T &);
/**
* popBack
*
* Removes the last index of the array
*
* Parameters:
* e: The value that was removed
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
MyVector<T> &popBack(T &);
/**
* front
*
* Returns the first element of the array
*
* Parameters: none
*
* Output:
* return: Copy of the first data item in the MyVector
* reference parameters: none
* stream: none
*/
T front();
/**
* back
*
* Returns the last element of the array
*
* Parameters: none
*
* Output:
* return: Returns a copy of the last data item in MyVector
* reference parameters: none
* stream: none
*/
T back();
/**
* [] operator
*
* Returns a reference to data element n in MyVector
*
* Parameters:
* n: index of item to return
*
* Output:
* return: Returns a reference to data element n in MyVector
* reference parameters: none
* stream: none
*/
T &operator[](int);
/**
* getSize
*
* Returns size of MyVector array
*
* Parameters: none
*
* Output:
* return: an integer value representing the number of elements in the list
* reference parameters: none
* stream: none
*/
int getSize();
/**
* isEmpty
*
* Returns state information about the list
*
* Parameters: none
*
* Output:
* return: Returns state information about the list
* reference parameters: none
* stream: none
*/
bool isEmpty();
/**
* erase
*
* Erases a vector
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
void erase();
private:
T *data;
int size;
int capacity;
/**
* grow
*
* Increases the capacity of data by doubling the previous value and allocating
* the appropriate memory for data
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
void grow();
/**
* shiftRight
*
* Shifts all values in the array one space to the right
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
void shiftRight();
/**
* shiftLeft
*
* Shifts all values in the array one space to the left
*
* Parameters: none
*
* Output:
* return: none
* reference parameters: none
* stream: none
*/
void shiftLeft();
};
template <class T>
MyVector<T>::MyVector()
{
this->size = 0;
this->capacity = 10;
this->data = new T[this->capacity];
}
template <class T>
MyVector<T>::~MyVector()
{
delete[] this->data;
}
template <class T>
MyVector<T>::MyVector(const MyVector &v)
{
this->size = v.size;
this->capacity = v.capacity;
this->data = new T[this->capacity];
// Copy each array item over
for (int i = 0; i < this->size; i++)
{
this->data[i] = v.data[i];
}
}
template <class T>
MyVector<T> &MyVector<T>::operator=(const MyVector &v)
{
this->size = v.size;
this->capacity = v.capacity;
this->data = new T[this->capacity];
// Copy each array item over
for (int i = 0; i < this->size; i++)
{
this->data[i] = v.data[i];
}
return *this;
}
template <class T>
MyVector<T> &MyVector<T>::pushFront(T e)
{
// Resize if necessary
if (this->size == this->capacity)
{
this->grow();
}
// Shift elements to the right
this->shiftRight();
// Add new value to first index of array
this->data[0] = e;
// Increment size
this->size++;
return *this;
}
template <class T>
MyVector<T> &MyVector<T>::pushBack(T e)
{
// Resize if necessary
if (this->size == this->capacity)
{
this->grow();
}
// Add value to array
this->data[this->size] = e;
// Increment size
this->size++;
return *this;
}
template <class T>
MyVector<T> &MyVector<T>::popFront(T &e)
{
// Throw BADINDEX if empty
if (this->size <= 0)
{
throw BADINDEX();
}
// Set e equal to the first value
e = this->front();
// Shift elements to the left removing the first index
this->shiftLeft();
// Decrement size
this->size--;
return *this;
}
template <class T>
MyVector<T> &MyVector<T>::popBack(T &e)
{
// Throw BADINDEX if empty
if (this->size <= 0)
{
throw BADINDEX();
}
// Set e equal to the last value
e = this->back();
// Remove last element by creating new array and copying values
T *temp = new T[this->capacity];
// Ignore last element and copy all values
for (int i = 0; i < this->size - 1; i++)
{
temp[i] = this->data[i];
}
// Deallocate current array
delete[] this->data;
// Allocate new temp array
this->data = temp;
// Decrement size
this->size--;
return *this;
}
template <class T>
T MyVector<T>::front()
{
// Throw BADINDEX if empty
if (this->size <= 0)
{
throw BADINDEX();
}
return this->data[0];
}
template <class T>
T MyVector<T>::back()
{
// Throw BADINDEX if empty
if (this->size <= 0)
{
throw BADINDEX();
}
return this->data[this->size - 1];
}
template <class T>
T &MyVector<T>::operator[](int n)
{
// Throw BADINDEX if n doesn't exist
if (n > this->size - 1)
{
throw BADINDEX();
}
return this->data[n];
}
template <class T>
int MyVector<T>::getSize()
{
return this->size;
}
template <class T>
bool MyVector<T>::isEmpty()
{
bool isEmpty = true;
// Check if size is greater than 0
if (this->size > 0)
{
isEmpty = true;
}
return isEmpty;
}
template <class T>
void MyVector<T>::erase()
{
// Erase vector by deallocating and allocating a new one
// Reset size & capacity
this->size = 0;
this->capacity = 10;
// Create new empty array
T *temp = new T[this->capacity];
// Delete old array
delete[] this->data;
// Set current array to new array
this->data = temp;
}
template <class T>
void MyVector<T>::grow()
{
// Double capacity as instructions say
this->capacity *= 2;
T *temp = new T[this->capacity];
// Copy each array item over
for (int i = 0; i < this->size; i++)
{
temp[i] = this->data[i];
}
// Deallocate current array
delete[] this->data;
// Allocate new temp array
this->data = temp;
}
template <class T>
void MyVector<T>::shiftRight()
{
// Create a new array
T *temp = new T[this->capacity];
// Copy values over shifting one to the right
for (int i = 1; i < this->size + 1; i++)
{
temp[i] = this->data[i - 1];
}
// Deallocate current array
delete[] this->data;
// Allocate new temp array
this->data = temp;
}
template <class T>
void MyVector<T>::shiftLeft()
{
// Create new array
T *temp = new T[this->capacity];
for (int i = 1; i < this->size; i++)
{
temp[i - 1] = this->data[i];
}
// Deallocate current array
delete[] this->data;
// Allocate new temp array
this->data = temp;
}
#endif
TEST FILE
#include <iostream>
#include "proj7-MyVector.h"
using namespace std;
int main()
{
cout << "MyVector Test" << endl;
cout << "Testing all functions using int MyVector, string MyVector, and double MyVector" << endl;
cout << endl;
cout << "Testing Default Constructor: ";
MyVector<int> intVector;
MyVector<string> stringVector;
MyVector<double> doubleVector;
cout << "Pass" << endl;
cout << "Testing pushFront: ";
intVector.pushFront(1);
stringVector.pushFront("test");
doubleVector.pushBack(1.32);
cout << "Pass" << endl;
cout << "Testing [] operator: ";
if (intVector[0] == 1 && stringVector[0] == "test" && doubleVector[0] == 1.32)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing pushBack: ";
intVector.pushBack(22);
stringVector.pushBack("hello");
doubleVector.pushBack(8.21);
cout << "Pass" << endl;
cout << "Testing back: ";
if (intVector.back() == 22 && stringVector.back() == "hello" && doubleVector.back() == 8.21)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing front: ";
if (intVector.front() == 1 && stringVector.front() == "test" && doubleVector.front() == 1.32)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing popFront: ";
int removedInt;
string removedString;
double removedDouble;
intVector.popFront(removedInt);
stringVector.popFront(removedString);
doubleVector.popFront(removedDouble);
if (removedInt == 1 && removedString == "test" && removedDouble == 1.32)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing getSize: ";
if (intVector.getSize() == 1 && stringVector.getSize() == 1 && doubleVector.getSize() == 1)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing popBack: ";
intVector.popBack(removedInt);
stringVector.popBack(removedString);
doubleVector.popBack(removedDouble);
if (removedInt == 22 && removedString == "hello" && removedDouble == 8.21)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing isEmpty: ";
if (intVector.isEmpty() && stringVector.isEmpty() && doubleVector.isEmpty())
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing = operator: ";
for (int i = 0; i < 10; i++)
{
intVector.pushBack(i);
stringVector.pushBack("a");
doubleVector.pushBack(2.5);
}
MyVector<int> intVector2;
MyVector<string> stringVector2;
MyVector<double> doubleVector2;
intVector2 = intVector;
stringVector2 = stringVector;
doubleVector2 = doubleVector;
if (intVector2.front() == 0 && stringVector2.front() == "a" && doubleVector2.front() == 2.5)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing copy constructor: ";
MyVector<int> intVector3(intVector2);
MyVector<string> stringVector3(stringVector2);
MyVector<double> doubleVector3(doubleVector2);
if (intVector2.front() == 0 && stringVector2.front() == "a" && doubleVector2.front() == 2.5)
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "Testing erase: ";
intVector3.erase();
stringVector3.erase();
doubleVector3.erase();
if (intVector3.isEmpty() && stringVector3.isEmpty() && doubleVector3.isEmpty())
{
cout << "Pass" << endl;
}
else
{
cout << "Fail" << endl;
}
cout << "If all of the above pass, grow(), shiftRight() and shiftLeft() are assumed passing." << endl;
return 0;
}
Upvotes: 1
Views: 83
Reputation: 118425
template <class T>
MyVector<T> &MyVector<T>::operator=(const MyVector &v)
{
this->size = v.size;
this->capacity = v.capacity;
this->data = new T[this->capacity];
The previously allocated this->data
gets leaked here. It's already allocated.
Furthermore, most of the class methods needlessly new
a new temp
buffer, copy data
to it, and delete
the old data
and then replace it with the newly-allocated temp
buffer.
This is needless work since, it looks like, most of these operations can be done in place. Furthermore, it appears that at least one of these has a bug that will result in memory corruption under certain conditions. In your shiftRight
:
for (int i = 1; i < this->size + 1; i++)
{
temp[i] = this->data[i - 1];
}
This is going to assign something to temp[this->size]
. If this->size
happens to be equal to this->capacity
, since temp
is allocated to be this->capacity
in size, this is going to result in a rather nasty demon flying out of your nose, since temp[this->size]
does not exist.
Upvotes: 1