正确管理内存——创建包含指针的双向链表

Correctly manage memory - Creating doubly linked lists containing pointers

(注意:我是新手。)我有一个创建双向链表的函数,其中包含代表*的 DNodes,每个代表有 3 个对象(字符串名称、int 访问、int 捐赠)和内存泄漏。

我在 运行 程序后收到以下内存泄漏消息:

https://textuploader.com/51tme

我在 valgrind 的内存检查中遇到了非常相似的错误。在我创建新代表的 5 次中,每条消息似乎都被触发了两次(总共有 10 次内存泄漏)。列出的函数总是 operator newaddToList() 中。这是 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,因为智能指针会处理它。