c ++中的动态数组:valgrind泄漏

时间:2018-06-07 00:16:55

标签: c++ valgrind destructor

我正在尝试用c ++实现动态数组。一旦它变满,它的大小加倍并被复制到双倍大小的数组。 看起来不错,但Valgrind说我有泄漏。 我应该说我不允许使用任何c ++ STL结构 - 这就是我使用new []和delete []运算符的原因。

这是我的代码:

[ 96%] Linking CXX executable mangosd
../game/libgame.a(Map.cpp.o): In function `sh_hashtable_settings<ObjectGuid, std::tr1::hash<ObjectGuid>, unsigned long, 4>::hash(ObjectGuid const&) const':
/usr/include/google/sparsehash/hashtable-common.h:65: undefined reference to `std::tr1::hash<ObjectGuid>::operator()(ObjectGuid) const'
collect2: error: ld returned 1 exit status
src/mangosd/CMakeFiles/mangosd.dir/build.make:244: recipe for target 'src/mangosd/mangosd' failed
make[2]: *** [src/mangosd/mangosd] Error 1
CMakeFiles/Makefile2:930: recipe for target 'src/mangosd/CMakeFiles/mangosd.dir/all' failed
make[1]: *** [src/mangosd/CMakeFiles/mangosd.dir/all] Error 2
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2

我的主要计划是:

template<class T>
class DynamicArray
{

    int size;
    int numOfElements;
    T* arr;

public:
    DynamicArray(int size) :
            size(size), numOfElements(0)
    {
        arr = new T[size];
    }
    ;

    int getSize()
    {
        return size;
    }

    int getNumberOfElements()
    {
        return numOfElements;
    }

    void insert(T element)
    {
        arr[numOfElements++] = element;
        if (numOfElements == size)
        {
            T* extended_array = new T[size * 2];
            for (int i = 0; i < numOfElements; i++)
            {
                extended_array[i] = arr[i];
            }
            delete[] arr;
            arr = extended_array;
            size = size * 2;
        }
    }

    T& operator[](int i)
    {
        if (!((i >= 0) && (i < size)))
            throw arrayOutOfBoundsException();
        return arr[i];
    }

    ~DynamicArray()
    {
        delete[] arr;
    }

    class arrayOutOfBoundsException: std::exception
    {
    };
};

** valgrind在将析构函数编译为注释时显示此错误:

using std::cout;
using std::endl;

void printIntArray(DynamicArray<int> arr){
    cout << "size: " << arr.getSize() << ";  " << "numOfElemnts: " << arr.getNumberOfElements() << endl;
    cout << "array: ";
    for(int i=0; i<arr.getNumberOfElements(); i++){
        cout << " " << arr[i];
    }
    cout << endl << endl;
}


int main() {

    DynamicArray<int> arr(5);
    printIntArray(arr);



    arr.insert(1);
    arr.insert(2);
    arr.insert(3);
    printIntArray(arr);

    arr.insert(4);
    printIntArray(arr);

    arr.insert(5);
    printIntArray(arr);

    arr.insert(6);
    printIntArray(arr);

    arr.insert(7);
    arr.insert(8);
    arr.insert(9);
    printIntArray(arr);

    arr.insert(16);
    printIntArray(arr);

    arr[9] = 901;
    arr[0] = 101;
    printIntArray(arr);

有谁能告诉我我做错了什么?我在滥用“删除[]”吗?还是析构函数?

感谢

1 个答案:

答案 0 :(得分:3)

提问者描述的行为是不可能的或

  

将析构函数编译为注释:

表示析构函数已被注释掉,这导致泄漏。这已经在评论中得到证实。没有destuctor,最后的分配永远不会被放弃。

Asker的真正问题是三违规。

短版本是这样的:

如果一个类需要用户定义的复制构造函数,赋值运算符或析构函数,它几乎总是需要这三个。

有关此内容的更多信息:What is The Rule of Three?

在这种情况下,

void printIntArray(DynamicArray<int> arr)

按值接受arr。这意味着arr是传入的值的副本。如果没有自定义复制构造函数,arr内的printIntArray将指向与源相同的分配。该副本将在printIntArray结束时销毁,析构函数将删除该分配,使源指向无效内存,因此......

DynamicArray<int> arr(5); // allocated storage inside arr
printIntArray(arr); // made a copy of arr that points to same storage. 
                    // Storage freed when copy destroyed

arr.insert(1); // invokes undefined behaviour writing into invalid memory
arr.insert(2); // same
arr.insert(3); // same
printIntArray(arr); // copies arr again. This copy is pointing to the same invalid memory
                    // and will try to free the allocation a second time when the 
                    // copy is destroyed

该程序几乎肯定会崩溃,因此无法从用户代码中泄露。但是因为程序崩溃了,Crom只知道运行时正在使用的幕后结构没有正确放弃。

无论发生什么,从

开始
arr.insert(1);

该程序深入Undefined Behaviour,之后发生的任何事情都是任何人的猜测。内存泄漏是您最不担心的事情。

修复:满足三条规则

实现复制构造函数和赋值运算符(We'll ignore the Rule of Five for now.

你可以改变

void printIntArray(DynamicArray<int> arr)

通过引用传递

void printIntArray(DynamicArray<int> & arr)

然后

DynamicArray(const DynamicArray &) = delete;
DynamicArray& operator=(const DynamicArray &) = delete;

并禁用所有复制,但这看起来有点严厉。

取而代之的是lets use Copy and Swap,因为它简单易懂。请注意,它也可能效率低下,因此并不总是正确的工具。这符合我们想要的。

DynamicArray(const DynamicArray & src)  :
        size(src.size), numOfElements(src.numOfElements), arr(new T[size])
{
    for (int i = 0; i < numOfElements; i++)
    {
        arr[i] = src.arr[i];
    }
}


DynamicArray& operator=(const DynamicArray src)
{
    std::swap(size, src.size);
    std::swap(numOfElements, src.numOfElements);
    std::swap(arr, src.arr);
    return *this;
}

因为谁想要复制,他们不需要我们仍然

void printIntArray(DynamicArray<int> & arr)

我们一旦完成测试,复制构造函数就能正常工作。