簡體   English   中英

返回std :: map時發生巨大的內存泄漏 <std::string, int> 來自取消引用的迭代器

[英]HUGE memory leak when returning std::map<std::string, int> from dereferenced iterator

**請注意** ,不僅該程序會掛起,而且顯然會永久占用您的所有內存,使您的計算機緩慢而混亂。 我已經為此苦苦掙扎了很長時間,並且已經弄清楚了很多事情,除了它為什么實際掛起的原因。 抱歉,有這么多代碼,但是我刪去了所有無關的內容,剩下的就是了。

鏈表

//=====================
// Linked List

#include <stdexcept>

template<class T> struct LinkedList {
    public:
        LinkedList();
        LinkedList(const LinkedList& srcList);
        ~LinkedList();

        void addObject (T& addedObject);

        class ListIterator {
            public:
                ListIterator();
                explicit ListIterator(LinkedList<T>& parentList);

                // Operators
                ListIterator& operator++();
                T& operator*() const;
                bool operator!=(const ListIterator& otherIter);

            private:
                typename LinkedList::Node* current_;
        };

        ListIterator begin();
        ListIterator end();
        std::size_t size_;

    private:
        struct Node {
            Node();
            Node(T& object);
            Node(const Node&) = delete;
            T* const object_;
            Node* next_;
            Node* prev_;
        };

        Node head_;
        Node tail_;
};

//====================
// Classes (Implementation)

// Linked List default constructor 
template<class T> LinkedList<T>::LinkedList() 
: size_{0} {
    head_.next_ = &tail_;
    tail_.prev_ = &head_;
};

// Linked List copy constructor
template<class T> LinkedList<T>::
LinkedList(const LinkedList& srcList) { 
    size_ = srcList.size_;
    head_.next_ = &tail_;
    tail_.prev_ = &head_;
    ListIterator nodesToCopy = srcList.begin();

    while (nodesToCopy != srcList.end()) { 
        this->addObject(*nodesToCopy);
        srcList.removeObject(1);
    };
    delete &srcList;
};

// Linked List destructor
template<class T> LinkedList<T>::~LinkedList() {
    for (unsigned int ii = 1; ii == size_; ++ii) {
        Node* toDelete = head_.next_;
        head_.next_ = head_.next_->next_;
        delete toDelete;
    };
};

// Add object to Linked List
template<class T> void LinkedList<T>::addObject(T& addedObject) {
    Node* node = new Node(addedObject);
    node->prev_ = tail_.prev_;
    tail_.prev_->next_ = node;
    tail_.prev_ = node;
    node->next_ = &tail_;
    ++size_;
};

// Linked List Iterator constructor
template<class T> LinkedList<T>::ListIterator::
ListIterator(LinkedList<T>& parentList) {
    current_ = parentList.head_.next_;
};

// Iterator operators
// Increment forward
template<class T> typename LinkedList<T>::ListIterator& LinkedList<T>::
ListIterator::operator++() {
    current_ = current_->next_;
    return *this;
};

// Return object pointed to
template<class T> T& LinkedList<T>::ListIterator::
operator*() const {
    return *(current_->object_);  
};

template<class T> bool LinkedList<T>::ListIterator::
operator!=(const ListIterator& otherIter) { 
    return &(**this) != &(*otherIter);
};

// Return an iterator object via begin() and end()
template<class T> typename LinkedList<T>::ListIterator
LinkedList<T>::begin() {
    ListIterator beginIterator(*this);
    return beginIterator;
};
template<class T> typename LinkedList<T>::ListIterator
LinkedList<T>::end() {
    ListIterator endIterator(*this);
    for (unsigned int ii = 0; ii < size_; ++ii) { ++endIterator; }; 
    return endIterator;
};

// Node constructors
template<class T> LinkedList<T>::Node::Node()
: object_(nullptr), next_(nullptr), prev_(nullptr) {};

template<class T> LinkedList<T>::Node::Node(T& object) 
: object_(&object) {};

項目

//=====================
// Item
//====================
// Included dependencies
#include <string>
#include <array>
#include <map>
#include <iostream>

class Item {
    public: 
        Item();
        Item(std::string name);
        Item(std::string name, std::array<int, 2> stats);
        std::map<std::string, int> getStats();

        std::string name_;

    private:
        std::map<std::string, int> enhancements_;
};

// Constructors
Item::Item() { 
    enhancements_["Str"] = 0;
    enhancements_["Def"] = 0;
};

Item::Item(std::string name) : Item::Item() { name_ = name; };
Item::Item(std::string name, std::array<int, 2> stats)
: Item::Item(name) {
    enhancements_["Str"] = stats[0];
    enhancements_["Def"] = stats[1];
};

// Return map of stats
std::map<std::string, int> Item::getStats() { return enhancements_; };

房間

//====================
// Room
class Room {
    public:
        void addItem(Item item);
        LinkedList<Item>::ListIterator getItems();
        LinkedList<Item> itemsInThisRoom_;
};

// Add item to room
void Room::addItem(Item item) { itemsInThisRoom_.addObject(item); };

// Get iterator which iterates over items in room
LinkedList<Item>::ListIterator Room::getItems() { 
    return itemsInThisRoom_.begin(); 
};

主要

int main() {
    std::array<int, 2> swordStats = {{5, 0}};
    std::array<int, 2> shieldStats = {{0, 2}};
    std::array<int, 2> armorStats = {{0, 3}};

    Item sword("Sword", swordStats);
    Item shield("Shield", shieldStats);
    Item armor("Armor", armorStats);
    Room room;

    room.addItem(shield);
    room.addItem(sword);
    room.addItem(armor);
    LinkedList<Item>::ListIterator roomItems = room.itemsInThisRoom_.begin();

    while (roomItems != room.itemsInThisRoom_.end()) {
        (*roomItems).getStats();
        ++roomItems;
    };

    return 0;
};

所有這些都可以放在一個文件中並進行編譯(我按類將其拆分以使其更易於閱讀)。 這是main掛起的行:

(*roomItems).getStats();

這使我相信我的解除引用運算符有問題,對嗎? 如果我們在Room類之外創建一個迭代器,請以相同的方式取消引用和getStats-一切正常。

...這是Room類的問題嗎?

但是,如果我們將Itemmain更改為以下內容:

//=====================
// Item
//====================
// Included dependencies
#include <string>
#include <array>
#include <map>
#include <iostream>

class Item {
    public: 
        Item();
        Item(std::string name);
        Item(std::string, int);
        int getStats();

        std::string name_;

    private:
        int enhancements_;
};

// Constructors
Item::Item() { 
    enhancements_ = 0;
};

Item::Item(std::string name) : Item::Item() { name_ = name; };
Item::Item(std::string name, int stats)
: Item::Item(name) {
    enhancements_ = stats;
};

// Return map of stats
int Item::getStats() { return enhancements_; };

//====================
// Room
class Room {
    public:
        void addItem(Item item);
        LinkedList<Item>::ListIterator getItems();
        LinkedList<Item> itemsInThisRoom_;
};

// Add item to room
void Room::addItem(Item item) { itemsInThisRoom_.addObject(item); };

// Get iterator which iterates over items in room
LinkedList<Item>::ListIterator Room::getItems() { 
    return itemsInThisRoom_.begin(); 
};

int main() {
    Item sword("Sword", 1);
    Item shield("Shield", 2);
    Item armor("Armor", 3);
    Room room;

    room.addItem(shield);
    room.addItem(sword);
    room.addItem(armor);
    LinkedList<Item>::ListIterator roomItems = room.itemsInThisRoom_.begin();
    while (roomItems != room.itemsInThisRoom_.end()) {
        (*roomItems).getStats();
        ++roomItems;
    };

    return 0;
};

一切運行良好。 我可以返回int值。

...所以... Room類或取消引用運算符都不是問題,而是返回 std :: map? GDB沒有太多話要說。 當我在令人討厭的行和步驟處中斷時,我得到:

24  std::map<std::string, int> Item::getStats() { return enhancements_; };
(gdb) step
_Rb_tree_impl (__a=<optimized out>, __comp=..., this=0x7fffffffced0)
    at /usr/include/c++/4.9/bits/stl_tree.h:474
474         _M_header(), _M_node_count(0)
(gdb) step
475       { _M_initialize(); }
(gdb) step
_M_initialize (this=0x7fffffffced0)
    at /usr/include/c++/4.9/bits/stl_tree.h:484
484         this->_M_header._M_left = &this->_M_header;
(gdb) step
485         this->_M_header._M_right = &this->_M_header;
(gdb) step
_Rb_tree (__x=..., this=0x7fffffffced0)
    at /usr/include/c++/4.9/bits/stl_tree.h:674
674     if (__x._M_root() != 0)
(gdb) step
_M_root (this=0x7fffffffd048)
    at /usr/include/c++/4.9/bits/stl_tree.h:498
498       { return this->_M_impl._M_header._M_parent; }
(gdb) step
_Rb_tree (__x=..., this=0x7fffffffced0)
    at /usr/include/c++/4.9/bits/stl_tree.h:674
674     if (__x._M_root() != 0)
(gdb) step
676         _M_root() = _M_copy(__x._M_begin(), _M_end());
(gdb) step
std::_Rb_tree<std::string, std::pair<std::string const, int>, std::_Select1st<std::pair<std::string const, int> >, std::less<std::string>, std::allocator<std::pair<std::string const, int> > >::_M_copy (
    this=this@entry=0x7fffffffced0, __x=0x619f10, 
    __p=__p@entry=0x7fffffffced8)
    at /usr/include/c++/4.9/bits/stl_tree.h:1207
1207          _Link_type __top = _M_clone_node(__x);

...這對我來說是胡說八道。 :(它無限執行此操作,所以我知道它(以某種方式)描述了掛斷。

知道這是怎么回事,哈哈。 我對C ++還是很陌生,自從醒來以來就一直為此而苦苦掙扎,因此我所知道的代碼非常糟糕,我應該為編寫它感到難過。

有任何想法嗎?

除了已經提到的內容外,您的Node對象還不固定地存儲指針,該指針是通過引用從外部傳遞的對象

template<class T> LinkedList<T>::Node::Node(T& object) 
: object_(&object) {};

但是,傳遞給Node的構造函數的參考參數實際上綁定到局部變量

template<class T> void LinkedList<T>::addObject(T& addedObject) {
    Node* node = new Node(addedObject);
    node->prev_ = tail_.prev_;
    tail_.prev_->next_ = node;
    tail_.prev_ = node;
    node->next_ = &tail_;
    ++size_;
};

void Room::addItem(Item item) { itemsInThisRoom_.addObject(item); };

也就是說,引用綁定到參數item ,后者是addItem內部的局部變量。

addItem退出后,該局部變量item被銷毀。 您的Node::object_指針仍然指向無處。

考慮到您在代碼中執行的無償復制的數量,完全不清楚您是如何想到在Node內部存儲指向非擁有對象的指針的想法的(而不是無償地將整個數據復制到Node ,就像您在其他任何地方所做的一樣)。

無論如何,內存所有權在您的代碼中被完全破壞了,這導致了對象生命周期問題。 您需要從頭開始設計一些有意義的內存所有權計划,然后按照該計划編寫代碼。 您現在擁有的是不可兌換的爛攤子。

如果您想使用指針而又不准備解開這種混亂,只需使用智能指針並讓它們為您處理事情即可。

PS並放下放置一個討厭的習慣; 在每個}

template<class T> LinkedList<T>::LinkedList(const LinkedList& srcList)

delete &srcList;

真? 只需刪除此行即可改善您的代碼。 &srcList不一定是已在堆上分配的地址。 並且在任何情況下,復制構造函數都不應刪除原始副本。

template<class T> LinkedList<T>::~LinkedList()

for (unsigned int ii = 1; ii == size_; ++ii)

除非列表的大小為1,否則此循環無效。

for (unsigned int ii = 0; ii < size_; ++ii)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM