簡體   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