从取消引用的迭代器返回 std::map<std::string, int> 时出现巨大的内存泄漏

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

本文关键字:std 泄漏 gt 内存 int 巨大 lt 迭代器 引用 取消 返回      更新时间:2023-10-16

**小心**,这个程序不仅会挂起,而且显然它会永远占用所有内存,使您的计算机变得缓慢而糟糕。我已经为此挣扎了很长时间,也弄明白了很多事情——除了为什么它会挂起来。对不起,有这么多的代码,但我删除了所有不相关的,这是剩下的。

LinkedList

//=====================
// 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) {};
<<p> 项/strong>
//=====================
// 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——一切都正常。

…所以这是房间课的问题?

但是,如果我们将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?广发银行没有太多话要说。当我在有问题的行和步骤处换行时,得到:

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内部的一个局部变量。

局部变量itemaddItem退出时被销毁。你的Node::object_指针仍然不指向任何地方。

考虑到您在代码中执行的免费复制的数量,完全不清楚您是如何设法在Node中存储指向非拥有对象的指针的想法的(而不是像您几乎在其他地方那样将整个数据免费复制到Node中)。

无论如何,内存所有权在你的代码中是完全被打破的,这导致了如上所述的对象生命周期问题。您需要从头开始设计一些有意义的内存所有权计划,然后按照该计划编写代码。你现在拥有的是无法挽回的烂摊子。

如果你想使用指针,而你又不认为你已经准备好去解决这些混乱,那就使用智能指针,让它们为你处理事情。

注:改掉在每个}后面都放一个;的坏习惯。

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

delete &srcList;

真的吗?只是删除这一行将改进您的代码。&srcList不一定是已经在堆上分配的地址。在任何情况下,复制构造函数都不应该删除原始的。

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

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

这个循环没有作用,除非列表的大小为1,它应该是

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