查找C ++内存泄漏

时间:2020-10-31 22:58:11

标签: c++ templates memory-leaks

我目前正在研究Vector类。我必须使用某些概念,例如模板等。在大多数情况下,我已经完成了整个项目,但内存泄漏使我无法定位。

我正在使用macOS Catalina,并且尝试安装Valgrind,但是似乎无法正常工作。这本身就是另一个问题。内存泄漏在哪里?对于macOS Catalina用户来说,检测内存泄漏在哪里的简便方法是什么?

我也在使用VS Code。

头文件

注意:ContainerIfc是一个抽象类,所有需要理解的方法都在下面实现。


#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

测试文件


#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;
}

1 个答案:

答案 0 :(得分:1)

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];

先前分配的this->data在这里泄漏。已经分配了。

此外,大多数类方法不需要new新的temp缓冲区,将data复制到其中,再delete旧的data然后替换使用新分配的temp缓冲区。

这是不必要的工作,因为看起来大多数这些操作都可以就地完成。此外,似乎其中至少有一个错误,在某些情况下会导致内存损坏。在您的shiftRight中:

for (int i = 1; i < this->size + 1; i++)
{
    temp[i] = this->data[i - 1];
}

这将为temp[this->size]分配内容。如果this->size恰好等于this->capacity,则由于temp的大小被分配为this->capacity,这将导致rather nasty demon flying out of your nose,因为{ {1}}不存在。

相关问题