繁体   English   中英

我不明白如何解决 memory 泄漏 C++ 的问题

[英]I can't understand how to solve problem with memory leak C++

健康)状况

在讲座中,我们已经开始实施我们的向量。 在此任务中,您需要对其进行开发:添加SizeCapacityPushBack方法。 发送包含SimpleVector class模板声明和定义的simple_vector.h header文件进行验证:

要求:

  • Capacity方法应返回向量的当前容量——适合向量当前分配的 memory 块的元素数
  • Size方法必须返回向量中元素的数量
  • PushBack方法将一个新元素添加到向量的末尾; 如果当前分配的 memory 块中没有可用空间(即Size() == Capacity() ),向量必须分配一个大小为2 * Capacity()的块,将所有元素复制到其中,并删除旧的一。
  • 对新创建的 object 的PushBack方法的第一次调用必须使容量等于 1
  • Push Back方法必须具有分摊的常量复杂度
  • beginend方法必须返回迭代器 vector 的当前开始和结束 vector 分配的当前 memory 块必须在析构函数中释放
  • 另请参阅随附的解决方案模板,了解在单元测试中使用SimpleVector的其他要求。

决定的准备:

评论

您发送用于验证的 header 文件不应包含<vector><list><forward_list><deque><map>文件。 如果启用了这些文件之一,则会出现编译错误。

提示

当然,您的SimpleVector class 模板的实现将有一个指针字段。 在默认构造函数中,您需要用一些东西来初始化它。 在讲座中,我们只讨论了一种初始化指针的方法——使用 new 运算符。 在 C++ 中,有一个特殊的值,表示指向任何内容的指针 — nullptr

int* p = nullptr;
string* q = nullptr;
map<string, vector<int>>* r = nullptr;

您可以使用 nullptr 在默认构造函数中初始化指针。

如何发送:当作品准备好后,您可以在“我的作品”选项卡上上传任务各部分的文件。

这是我的.h解决方案,Coursera 测试系统响应10:= 8: Memory leak detected 但是我不知道泄漏在哪里。 请帮助我。

#pragma once
#include <cstdlib>

using namespace std;

template <typename T>
class SimpleVector {
public:
    SimpleVector() 
        : data(nullptr)
        , end_(data)
        , size_(0) {}

    explicit SimpleVector(size_t size) 
        : data(new T[size])
        , end_(data + size)
        , size_(size) {}

    ~SimpleVector() {
        delete[] data;
    }

    T& operator[](size_t index) { return data[index]; }

    T* begin() const { return data; }
    T* end() const { return end_; }

    size_t Capacity() const { return end_ - data; }
    
    size_t Size() const { return size_; }

    void PushBack(const T& value) {
        if (size_ == Capacity()) {
            if (size_ == 0) {
                delete[] data;  
                data = new T[1];
                data[size_] = value;
                ++size_; 
                end_ = data + size_;
            }
            else {
                T* local_data = new T[size_];
                
                for (size_t i = 0; i < size_; ++i) {
                    local_data[i] = data[i];
                }

                delete[] data;
                data = new T[2 * Capacity()];
                
                for (size_t i =0; i < size_; ++i) {
                    data[i] = local_data[i];
                }
                delete[] local_data;
                
                data[size_] = value;
                ++size_;
                end_ = data + size_ * 2;
            }
        } 
        else {      
            data[size_] = value;
            size_++;
        }
    }
private:
    T *data;
    T *end_;
    size_t size_;
};

提前谢谢你

由于缺乏异常安全性, PushBack中存在 memory 泄漏。 考虑:

T* local_data = new T[size_];
// potentially throwing operations here...
delete[] local_data;

如果这些操作抛出,则delete[] local_data; 永远不会被执行。

避免这种 memory 泄漏的典型方法是使用智能指针而不是裸指针来获取所有权。 过时的方法是使用 try-catch。


您的 class 也无法强制执行data指针唯一性的 class 不变量。 这样的约束对于析构函数的正确性是必不可少的,因为分配必须恰好被删除一次,不能再多了。

复制 class 的实例将导致未定义的行为,因为在多个析构函数中删除了相同的指针。 另一个结果是赋值运算符将泄漏先前分配的 memory(在析构函数中出现 UB 之前):

{
    SimpleVector vec(42);
    SimpleVector another(1337);
    SimpleVector vec = another; // memory leak in assignment operator
} // undefined behaviour in the destructor

问题出在复制和移动构造函数和赋值运算符中,您将其保留为隐式生成。 隐式生成的特殊成员函数将复制指针值,违反其唯一性(并且在赋值的情况下无法删除之前的分配)。 换句话说,这些函数执行浅拷贝。

使用智能指针作为成员是一个简单的解决方案。 否则,您必须实现不泄漏也不违反唯一性的复制和移动构造函数和赋值运算符。

请注意,即使您确实使用了智能指针,由于end指针,您仍然需要用户定义的副本等。 如果您改为使用与data相关的 integer,则可以避免定义这些函数。


PS 不需要分配两次,复制两次。 相反,分配一个更大的缓冲区,复制旧的,删除旧的,指向新的。


PPS 作为旁注:您正在实施的向量与标准向量的行为完全不同,这可能是您的老师有意为之。 当我将 object 添加到包含 10 个元素的向量时,我希望只创建一个元素,并且可能由于重定位而复制 10 个,而不是创建 20 个对象,其中 9 个不可访问。

vector 的正确实现将 memory 的分配和对象的创建分离到 memory 中,这允许 memory 的增长是几何的,而无需创建对象,直到它们被添加到 vector 中。 我怀疑如何做到这一点在你练习的 scope 之外。

我不会称其为泄漏,但您对待end_的方式不一致。 似乎您将SizeCapacity视为等效值,但事实并非如此。

要么end_应该指向分配的(但不一定填充)memory,并且您在end()中返回data + size ,或者它应该指向最后一个元素,并且您应该存储size_t capacity_而不是size_t size_

这是没有 memory 泄漏的解决方案。 谢谢你。

#pragma once
#include <cstdlib>

using namespace std;

template <typename T>
class SimpleVector {
public:
    SimpleVector() {
        data_ = nullptr;
        end_ = data_;
        size_ = 0;
        capacity_ = 0;
    }
    explicit SimpleVector(size_t size) {
        data_ = new T[size];
        end_ = data_ + size;
        size_ = size;
        capacity_ = size;
    }

    SimpleVector(const SimpleVector& that) 
        : data_(that.data_)
        , end_(that.end_)
        , size_(that.size_)
        , capacity_(that.capacity_) {}

    SimpleVector& operator = (const SimpleVector& that) {
        data_ = that.data_;
        end_ = that.end_;
        size_ = that.size_;
        capacity_ = that.capacity_;
    }

    ~SimpleVector() { delete[] data_; }

    T& operator[](size_t index) {
        return data_[index];
    }

    T* begin() const { return data_; }
    T* end() const { return data_ + size_; }

    size_t Capacity() const { return capacity_; }
    
    size_t Size() const { return size_; }

    void PushBack(const T& value) {
        if (size_ == capacity_) {
            if (capacity_ == 0) { // т. е. создали конструктором по умолчанию, size_ = 0
                data_ = new T[1];
                capacity_ = 1;
                data_[size_] = value;
                ++size_; 
                end_ = data_ + size_;
            }
            else if (capacity_ == size_) { // т. е. capacity_ == size_
                T* local_data = new T[2 * size_];
                for (size_t i = 0; i < size_; ++i) {
                    local_data[i] = data_[i];
                }
                
                delete[] data_;
                data_ = new T[2 * size_];
                for (size_t i = 0; i < size_; ++i) {
                    data_[i] = local_data[i];
                }
                delete[] local_data;
                data_[size_] = value;
                size_++;
                capacity_ *= 2;
                end_ = data_ + size_;
            }
        } 
        else {      
            data_[size_] = value;
            size_++;
        }
    }
private:
    T *data_;
    T *end_;
    size_t size_;
    size_t capacity_;
};

暂无
暂无

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

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