简体   繁体   English

我如何解决这个内存泄漏

[英]How do I solve this memory leak

I am trying to recreate the vector class and I believe there is a memory leak in my code, but I don't know how to solve it.我正在尝试重新创建向量类,我相信我的代码中存在内存泄漏,但我不知道如何解决。 Using the CRT Library in my Visual Studio, it tells me that there is a supposed memory leak that doubles for each time that reserve is called.在我的 Visual Studio 中使用 CRT 库,它告诉我有一个假设的内存泄漏,每次调用该保留时都会增加一倍。

I am not quite sure why that is or if there even is a memory leak.我不太确定为什么会这样,或者是否有内存泄漏。 The memory leak detection says that it is this line in the reserve function int* temp = new int[n];内存泄漏检测说它是保留函数中的这一行int* temp = new int[n];

This is what I understand to be happening in the reserve function:这就是我所理解的在储备功能中发生的事情:

Once the contents of arr are copied into temp, it's fine to delete arr.一旦arr的内容复制到temp中,删除arr就可以了。 Assigning arr = temp should work because all I'm doing is making arr point to the same place as temp.分配arr = temp应该可以工作,因为我所做的只是让 arr 指向与 temp 相同的位置。 Because arr was previously deleted, I only have 1 array in the heap and arr and temp both point to the same array so there should be no memory leak.因为 arr 之前被删除了,所以我在堆中只有 1 个数组,并且 arr 和 temp 都指向同一个数组,所以应该没有内存泄漏。 Temp shouldn't matter because it disappears after it exits the scope. Temp 应该无关紧要,因为它在退出范围后就会消失。 On subsequent calls to the reserve function, every thing repeats and there should only be one array in the heap which arr points to.在对保留函数的后续调用中,每件事都会重复,并且堆中应该只有一个数组 arr 指向。

I do believe that my thinking is probably erroneous in some way.我确实相信我的想法可能在某些方面是错误的。

    #include "Vector.h"

    namespace Vector {

    vector::vector() {
        sz = 0;
        space = 0;
        arr = nullptr;
    }
    vector::vector(int n) {
        sz = n;
        space = n;
        arr = new int[n];
        for(int i = 0; i < n; i++) {
            arr[i] = 0;
        }
    }
    void vector::push_back(int x) {
        if(sz == 0) {
            reserve(1);
        } else if (sz == space) {
            reserve(2*space);
        }
        arr[sz] = x;
        sz++;
    }
    void vector::reserve(int n) {
        if (n == 1) {
            arr = new int[1]; //arr was a nullptr beforehand
        }
        int* temp = new int[n]; 
        for(int i = 0; i < n; i++) {
            temp[i] = arr[i];
        }
        delete[] arr;
        arr = temp;
        space = n;      
    }

Your code assumes in vector::reserve(int n) that arr is null .您的代码在vector::reserve(int n)假设arrnull

Instead maybe spilt up how reserve functions based on whether or not arr is null .相反,可能会根据arr是否为null来详细说明reserve函数的方式。

void vector::reserve(int n) {
    if (arr) { //handle case when arr is null
        space += n;
        arr = new int[space];
        //no need to copy anything!
    } else { //handle case when arr is not null
        int* tmp(new int[space + n]);
        for(int i = 0; i < space; i++) {
            tmp[i] = arr[i];
        }
        delete[] arr;
        arr = tmp;
        space += n;
    }
}

Also the above code assumes you mean to reserve space+n instead of allowing reserve to shrink the array as you'll lose data if you reserve less then a previous reserve.此外,上面的代码假设您的意思是保留space+n而不是允许reserve来缩小数组,因为如果您保留的数量少于先前的保留,您将丢失数据。 It's usually better practice to not use assumptions about a pointer's state when working with them because when your code gets more complex the assumptions can end up getting forgotten or more obscure.通常更好的做法是在使用指针时不使用关于指针状态的假设,因为当您的代码变得更复杂时,这些假设最终可能会被遗忘或更模糊。

I have same issues too.我也有同样的问题。 I have created two pointers that points in the same address in heap.我创建了两个指向堆中同一地址的指针。 When I'm trying too deallocate the memory, and the result is only one pointer that can do that, it's the first pointers that point that address.当我试图释放内存时,结果只有一个指针可以做到这一点,它是指向该地址的第一个指针。 The second or third pointers that points that address doesn't have an authority to deallocate the memory, but only the first pointers who have that authority.指向该地址的第二个或第三个指针没有释放内存的权限,但只有第一个拥有该权限的指针。

Example例子

int *a = new int[5];
int *b = a;
int *c = a;

Pointers b and c doesn't have an authority too dealloacte the memory address that pointers a pointed.指针 b 和 c 没有权限释放指针 a 所指向的内存地址。 Therefore, the memory wasn't deallocated if i'm saying delete[] b nor delete[] c , they didn't have an authority for doing that.因此,如果我说的是delete[] bdelete[] c ,则内存不会被释放,他们没有这样做的权限。 Then I tried to write delete [] a and that worked.然后我试着写delete [] a并且奏效了。 I don't have an real answers, and I just trying to approaching through my try and errors that I have done.我没有真正的答案,我只是试图通过我所做的尝试和错误来解决。 And that's what I got.这就是我得到的。

Actually this case is violating the rules, but C++ still allowed us to do it, it's called undefined behaviors .其实这种情况是违反规则的,但是C++仍然允许我们这样做,它被称为未定义行为 We are violating the rules of delete[] operators by, but C++ still allowed you to do, and as the result, you get unexpected output.我们违反了delete[]运算符的规则,但 C++ 仍然允许您这样做,结果,您得到了意外的输出。

Not too much wrong in there.那里没有太多错。

Bugs错误

    if (n == 1) {
        arr = new int[1]; //arr was a nullptr beforehand
    }

The comment cannot be guaranteed.不能保证评论。 Nothing prevents multiple calls of resize including a call of reserve(1) , and that will leak whatever memory was pointed at by arr .没有什么可以阻止多次调用调整大小,包括调用reserve(1) ,这将泄漏arr指向的任何内存。 Instead consider而是考虑

    if (arr == nullptr) {
        arr = new int[n]; //arr was a nullptr beforehand
    }

now the comment is guaranteed to be true.现在评论保证是真实的。

The copy loop overshoots the end of arr every time the size of the array is increased.每次增加数组的大小时,复制循环都会超过arr的结尾。

    for(int i = 0; i < n; i++) {
        temp[i] = arr[i];
    }

arr is only good up to arr[sz-1] . arr只适用于arr[sz-1] If n is greater than space , and it almost always will be, arr[i] wanders into the great wilds of Undefined Behaviour .如果n大于space ,而且几乎总是这样, arr[i]就会游荡到Undefined Behavior 的荒野中。 Not a good place to go.不是一个好去处。

    for(int i = 0; i < n && i < sz; i++) {
        temp[i] = arr[i];
    }

Checks both n and sz to prevent overrun on either end and copying of data that has not been set yet.检查nsz以防止任一端溢出和复制尚未设置的数据。 If there is nothing to be copied, all done.如果没有什么可复制的,则全部完成。

Targets of opportunity机会目标

The class needs a destructor to release any memory that it owns ( What is ownership of resources or pointers? ) when it is destroyed.类需要一个析构函数来释放它拥有的任何内存( 资源或指针的所有权是什么? )。 Without it, you have a leak.没有它,你就有泄漏。

vector::~vector() {
    delete[] arr;
}

And if it has a destructor, the Rule of Three requires it to have special support functions to handle (at least) copying of the class or expressly forbid copying.如果它有析构函数,则三规则要求它具有特殊的支持函数来处理(至少)类的复制或明确禁止复制。

// exchanges one vector for the other. Generally useful, but also makes moves 
// and assignments easy
void vector::swap(vector& a, vector& b)
{
    std::swap(a.sz, b.sz);
    std::swap(a.space, b.space);
    std::swap(a.arr, b.arr);
}

// Copy constructor
vector::vector(const vector& src):
    sz(src.sz),
    space (src.space),
    arr(new int[space])
{
    for(int i = 0; i < sz; i++) {
        arr[i] = src.arr[i];
    }
}

// move constructor
vector::vector(vector&& src): vector()
{
    swap(*this, src);
}

// assignment operator
vector::vector& vector::operator=(vector src)
{
    swap(*this, src);

    return *this;
}

The Copy Constructor uses a Member Initializer List . Copy Constructor 使用Member Initializer List That's the funny : bit.这很有趣:位。

The assignment operator makes use of the Copy and Swap Idiom .赋值运算符使用Copy and Swap Idiom This isn't the fastest way to implement the assignment operator, but it is probably the easiest.这不是实现赋值运算符的最快方法,但它可能是最简单的方法。 Start with easy and only go to hard if easiest doesn't meet the requirements.从简单开始,只有在最简单的不满足要求时才进行困难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM