从取消引用的迭代器返回 std::map<std::string, int> 时出现巨大的内存泄漏
HUGE memory leak when returning std::map<std::string, int> from dereferenced iterator
**小心**,这个程序不仅挂了,而且显然它永远占用了你的 all 内存,渲染您的计算机速度缓慢且一团糟。我已经为此苦苦挣扎了很长时间,并且已经弄明白了很多事情——除了它为什么会挂起。抱歉,代码太多了,但是我删掉了所有不相关的东西,剩下的就是这个。
链表
//=====================
// 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;
};
所有这些都可以放在一个文件中并编译(我将其拆分为 class 以便于阅读)。这是 main 中挂起的行:
(*roomItems).getStats();
这让我相信我的取消引用运算符有问题,对吧?如果我们在房间 class 之外创建一个迭代器 outside,取消引用它,并以相同的方式 getStats — 一切正常。
...所以这是房间 class 的问题?
但是,如果我们将 Item 和 main 更改为以下内容:
//=====================
// 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;
};
一切运行良好。我可以 return int 值。
...所以...房间 class 或取消引用运算符都不是问题,但是 returning 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++ 的新手,自从我醒来后就一直在为此苦苦挣扎,所以据我所知,我的代码很糟糕,我应该为编写它而感到难过。
有什么想法吗?
来自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)
除了已经提到的内容之外,您的 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
,就像您在其他任何地方所做的一样)。
无论如何,内存所有权在您的代码中完全被破坏,这会导致上述对象生命周期问题。您需要从头开始设计一些有意义的内存所有权计划,然后按照该计划编写代码。你现在拥有的是不可挽回的烂摊子。
如果你想使用指针,但你认为你还没有准备好解决这个问题,只需使用智能指针,让它们为你处理事情。
P.S。并改掉在每个 }
.
之后放置 ;
的恶习
**小心**,这个程序不仅挂了,而且显然它永远占用了你的 all 内存,渲染您的计算机速度缓慢且一团糟。我已经为此苦苦挣扎了很长时间,并且已经弄明白了很多事情——除了它为什么会挂起。抱歉,代码太多了,但是我删掉了所有不相关的东西,剩下的就是这个。
链表
//=====================
// 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;
};
所有这些都可以放在一个文件中并编译(我将其拆分为 class 以便于阅读)。这是 main 中挂起的行:
(*roomItems).getStats();
这让我相信我的取消引用运算符有问题,对吧?如果我们在房间 class 之外创建一个迭代器 outside,取消引用它,并以相同的方式 getStats — 一切正常。
...所以这是房间 class 的问题?
但是,如果我们将 Item 和 main 更改为以下内容:
//=====================
// 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;
};
一切运行良好。我可以 return int 值。
...所以...房间 class 或取消引用运算符都不是问题,但是 returning 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++ 的新手,自从我醒来后就一直在为此苦苦挣扎,所以据我所知,我的代码很糟糕,我应该为编写它而感到难过。
有什么想法吗?
来自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)
除了已经提到的内容之外,您的 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
,就像您在其他任何地方所做的一样)。
无论如何,内存所有权在您的代码中完全被破坏,这会导致上述对象生命周期问题。您需要从头开始设计一些有意义的内存所有权计划,然后按照该计划编写代码。你现在拥有的是不可挽回的烂摊子。
如果你想使用指针,但你认为你还没有准备好解决这个问题,只需使用智能指针,让它们为你处理事情。
P.S。并改掉在每个 }
.
;
的恶习