正确管理内存——创建包含指针的双向链表
Correctly manage memory - Creating doubly linked lists containing pointers
(注意:我是新手。)我有一个创建双向链表的函数,其中包含代表*的 DNodes,每个代表有 3 个对象(字符串名称、int 访问、int 捐赠)和内存泄漏。
我在 运行 程序后收到以下内存泄漏消息:
https://textuploader.com/51tme
我在 valgrind 的内存检查中遇到了非常相似的错误。在我创建新代表的 5 次中,每条消息似乎都被触发了两次(总共有 10 次内存泄漏)。列出的函数总是 operator new 在 addToList() 中。这是 addToList() 函数:
bool ListManager::addToList(string rep_list){
istringstream convertRepresentative(rep_list);
string whitespaces(" \t\f\v\n\r");
size_t found = rep_list.find_first_not_of(whitespaces);
if (rep_list.empty() || found == string::npos){
/* string is empty or only has whitespace */
cout << "\n| ERROR(addToList()): String is empty. No representatives added to list.\n| Function halted.\n";
return false;
}
string name;
int visits;
int donations;
Representative* temp; // comment out if using shared_ptr
if (convertRepresentative.fail()){
/* error in string input */
cout << "\n| ERROR(addToList()): Could not import string. No representatives added to list.\n| Function halted.\n";
convertRepresentative.clear();
return false;
}
else{
while (!convertRepresentative.eof()){
if (!(convertRepresentative >> name)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
if (!(convertRepresentative >> visits)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
if (!(convertRepresentative >> donations)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
temp = new Representative(name, visits, donations);
ListDLL.insertTail(temp);
//shared_ptr<Representative> temp;
//temp.reset(new Representative(name, strength, speed));
//RosterDLL.insertTail(&*temp);
}
convertRepresentative.clear();
return true;
}
}
我假设我将在某个时候删除临时文件,但删除临时文件将从 DNode 中删除代表数据。所以我不知道什么时候删除 temp and/or reset/clear 它,无论如何。正如您在我的注释代码中看到的那样,我已经尝试过智能指针,但它们会执行我刚才描述的操作——它们会从 DNode 中删除所需的信息。
我是新手,我知道你们对格式等很严格(抱歉我的#include namespace std)。我希望这是一个简单的修复。请帮忙。
编辑:
根据建议,这里是 DoubleLinkedList.h(您可以在第一个 link 中看到该项目使用的所有文件):
#ifndef DOUBLELINKEDLIST_H_
#define DOUBLELINKEDLIST_H_
#include <stdexcept>
#include <iostream>
#include "Representative.h"
using namespace std;
struct DNode{
Representative* rep;
DNode* next;
DNode* prev;
DNode(Representative*& rep,
DNode* prev_val = NULL, DNode* next_val = NULL) :
rep(rep), next(next_val), prev(prev_val) {}
};
template<class T>
class DoubleLinkedList
{
public:
DNode* head;
DNode* tail;
int count;
DoubleLinkedList() :
head(NULL), count(0),
tail(NULL){
}
virtual ~DoubleLinkedList() {
clear();
}
bool find(Representative* rep){
bool found = false;
if (head == NULL) {
return found;
}
DNode* DNode_ptr = head;
while (DNode_ptr != NULL){
if (DNode_ptr->rep == rep){
found = true;
}
DNode_ptr = DNode_ptr->next;
}
return found;
}
void insertTail(Representative* rep) {
if (find(rep) == false) {
if (head == NULL) {
DNode* newDNode = new DNode(rep);
head = newDNode;
count++;
return;
}
else {
DNode* temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
DNode* tailDNode = new DNode(rep);
temp->next = tailDNode;
tailDNode->next = NULL;
tailDNode->prev = temp;
count++;
return;
}
}
else {
return;
}
}
Representative* at(int index){
DNode* temp_ptr = head;
if (index < 0 || index > count - 1){
throw out_of_range("Out of Range");
}
else {
for (int i = 0; i < index; i++){
temp_ptr = temp_ptr->next;
}
return temp_ptr->rep;
}
}
int size(){
return count;
}
void clear(){
DNode* temporary_ptr = head;
while (temporary_ptr != NULL){
DNode* tmp_pointer = temporary_ptr;
temporary_ptr = temporary_ptr->next;
delete tmp_pointer;
}
count = 0;
head = NULL;
return;
}
//Rest of functions omitted
};
#endif /* DOUBLELINKKEDLIST_H_ */
啊,我现在明白了。您分配一个 Representative
,然后将该分配的指针存储在一个分配的 DNode
中。当您 clear
DoubleLinkedList
时,将删除 DNodes
但不会删除存储在其中的已分配 Representatives
,这会导致泄漏。
更好的解决方案是在列表中存储 Representative
,而不是 Representative *
。
如果你可以在 C++11 下编译,考虑到编译器高度支持它,你应该可以,你根本不应该使用原始指针来管理内存。
如果您不想传输大量数据,而是自己制作 class,您甚至不需要一开始就使用指针。只需实现移动构造函数和移动赋值运算符并使用常规值 objects 而不是指向它们的指针。
如果您确实需要使用指针 - 例如可以制作 objects 的 collection 某种库 objects,它没有移动构造函数并实现移动赋值运算符 - 使用 <memory>
header 中的 std::unique_ptr<T>
或 std::shared_ptr<T>
。您不需要调用 delete,因为智能指针会处理它。
(注意:我是新手。)我有一个创建双向链表的函数,其中包含代表*的 DNodes,每个代表有 3 个对象(字符串名称、int 访问、int 捐赠)和内存泄漏。
我在 运行 程序后收到以下内存泄漏消息:
https://textuploader.com/51tme
我在 valgrind 的内存检查中遇到了非常相似的错误。在我创建新代表的 5 次中,每条消息似乎都被触发了两次(总共有 10 次内存泄漏)。列出的函数总是 operator new 在 addToList() 中。这是 addToList() 函数:
bool ListManager::addToList(string rep_list){
istringstream convertRepresentative(rep_list);
string whitespaces(" \t\f\v\n\r");
size_t found = rep_list.find_first_not_of(whitespaces);
if (rep_list.empty() || found == string::npos){
/* string is empty or only has whitespace */
cout << "\n| ERROR(addToList()): String is empty. No representatives added to list.\n| Function halted.\n";
return false;
}
string name;
int visits;
int donations;
Representative* temp; // comment out if using shared_ptr
if (convertRepresentative.fail()){
/* error in string input */
cout << "\n| ERROR(addToList()): Could not import string. No representatives added to list.\n| Function halted.\n";
convertRepresentative.clear();
return false;
}
else{
while (!convertRepresentative.eof()){
if (!(convertRepresentative >> name)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
if (!(convertRepresentative >> visits)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
if (!(convertRepresentative >> donations)){
cout << "\n| ERROR(addToList()): Could not import string. Incorrect format.\n| No representatives added to list. Function halted.\n";
rep_list.clear();
convertRepresentative.clear();
return false;
}
temp = new Representative(name, visits, donations);
ListDLL.insertTail(temp);
//shared_ptr<Representative> temp;
//temp.reset(new Representative(name, strength, speed));
//RosterDLL.insertTail(&*temp);
}
convertRepresentative.clear();
return true;
}
}
我假设我将在某个时候删除临时文件,但删除临时文件将从 DNode 中删除代表数据。所以我不知道什么时候删除 temp and/or reset/clear 它,无论如何。正如您在我的注释代码中看到的那样,我已经尝试过智能指针,但它们会执行我刚才描述的操作——它们会从 DNode 中删除所需的信息。
我是新手,我知道你们对格式等很严格(抱歉我的#include namespace std)。我希望这是一个简单的修复。请帮忙。
编辑: 根据建议,这里是 DoubleLinkedList.h(您可以在第一个 link 中看到该项目使用的所有文件):
#ifndef DOUBLELINKEDLIST_H_
#define DOUBLELINKEDLIST_H_
#include <stdexcept>
#include <iostream>
#include "Representative.h"
using namespace std;
struct DNode{
Representative* rep;
DNode* next;
DNode* prev;
DNode(Representative*& rep,
DNode* prev_val = NULL, DNode* next_val = NULL) :
rep(rep), next(next_val), prev(prev_val) {}
};
template<class T>
class DoubleLinkedList
{
public:
DNode* head;
DNode* tail;
int count;
DoubleLinkedList() :
head(NULL), count(0),
tail(NULL){
}
virtual ~DoubleLinkedList() {
clear();
}
bool find(Representative* rep){
bool found = false;
if (head == NULL) {
return found;
}
DNode* DNode_ptr = head;
while (DNode_ptr != NULL){
if (DNode_ptr->rep == rep){
found = true;
}
DNode_ptr = DNode_ptr->next;
}
return found;
}
void insertTail(Representative* rep) {
if (find(rep) == false) {
if (head == NULL) {
DNode* newDNode = new DNode(rep);
head = newDNode;
count++;
return;
}
else {
DNode* temp = head;
while (temp->next != NULL) {
temp = temp->next;
}
DNode* tailDNode = new DNode(rep);
temp->next = tailDNode;
tailDNode->next = NULL;
tailDNode->prev = temp;
count++;
return;
}
}
else {
return;
}
}
Representative* at(int index){
DNode* temp_ptr = head;
if (index < 0 || index > count - 1){
throw out_of_range("Out of Range");
}
else {
for (int i = 0; i < index; i++){
temp_ptr = temp_ptr->next;
}
return temp_ptr->rep;
}
}
int size(){
return count;
}
void clear(){
DNode* temporary_ptr = head;
while (temporary_ptr != NULL){
DNode* tmp_pointer = temporary_ptr;
temporary_ptr = temporary_ptr->next;
delete tmp_pointer;
}
count = 0;
head = NULL;
return;
}
//Rest of functions omitted
};
#endif /* DOUBLELINKKEDLIST_H_ */
啊,我现在明白了。您分配一个 Representative
,然后将该分配的指针存储在一个分配的 DNode
中。当您 clear
DoubleLinkedList
时,将删除 DNodes
但不会删除存储在其中的已分配 Representatives
,这会导致泄漏。
更好的解决方案是在列表中存储 Representative
,而不是 Representative *
。
如果你可以在 C++11 下编译,考虑到编译器高度支持它,你应该可以,你根本不应该使用原始指针来管理内存。
如果您不想传输大量数据,而是自己制作 class,您甚至不需要一开始就使用指针。只需实现移动构造函数和移动赋值运算符并使用常规值 objects 而不是指向它们的指针。
如果您确实需要使用指针 - 例如可以制作 objects 的 collection 某种库 objects,它没有移动构造函数并实现移动赋值运算符 - 使用 <memory>
header 中的 std::unique_ptr<T>
或 std::shared_ptr<T>
。您不需要调用 delete,因为智能指针会处理它。