C 中的学生指针节点列表中的内存泄漏



我有一个节点列表,每个节点都包含一个指向学生变量(这是一个类(的指针,而指向下一个节点的指针。这是我的插入代码。

void studentRoll::insertAtTail(const Student &s) {
    if (head == NULL) {
        this->head = new Node;
        this->head->next = NULL;
        this->head->s = new Student(s);
        this->tail = head;
    }
    else {
        this->tail->next = new Node;
        this->tail = this->tail->next;
        this->tail->next = NULL;
        this->tail->s = new Student(s);
    }
}

我用valgrind进行调试,我得到了:

==11106== 16 bytes in 1 blocks are definitely lost in loss record 1 of 2
==11106==    at 0x4C2D1CA: operator new(unsigned long) 
(vg_replace_malloc.c:334)
==11106==    by 0x402BE7: StudentRoll::insertAtTail(Student const&) 
(studentRoll.cpp:15)
==11106==    by 0x401CF1: main (testStudentRoll01.cpp:19)
==11106==
==11106== 16 bytes in 1 blocks are definitely lost in loss record 2 of 2
==11106==    at 0x4C2D1CA: operator new(unsigned long) 
(vg_replace_malloc.c:334)
==11106==    by 0x402C5B: StudentRoll::insertAtTail(Student const&) 
(studentRoll.cpp:22)
==11106==    by 0x401E2C: main (testStudentRoll01.cpp:27)
==11106==

有人可以帮我吗?我认为有一些问题:

this->head->s = new Student(s);

this->tail->s = new Student(s);

,但我无法删除它们,因为我需要这些"学生"。而且有指示指向"学生"。

谢谢!

更新:这是我的destructor

StudentRoll::~StudentRoll() {
    Node *iter = head;
    while (iter) {
        Node *next = iter->next;
        iter->s->~Student();
        delete iter;
        iter = next;
    }
    head = tail = NULL;
}

有人可以帮我吗?我认为有一些问题:

this->head->s = new Student(s);

this->tail->s = new Student(s);

但我无法删除它们,因为我需要这些"学生"。有指示指向"学生"。"

此问题可能表明您应该重新设计程序。在C 中,您应该表达所有权语义,并清楚哪些对象拥有哪些资源并负责其清理。C 中的所有权语义通过各种指针类型表示:

如果一个特定的单个对象拥有一些堆内存,而不是直接使用原始指针,而newdelete则使用std::unique_ptrstd::unique_ptr更好,因为它传达了您对读者的意图,并使用RAII来防止记忆泄漏。

另一方面,如果对象没有内存,请改用参考或原始指针。(将来,C 标准库可能会得到一个非拥有智能指针。(

如果您的链接列表数据结构拥有学生对象,则应该是对它们进行处理的对象。在这种情况下,使用std::unique_ptr

void studentRoll::insertAtTail(const Student &s) {
    if (head.get() == nullptr) {
        this->head = std::make_unique<Node>();
        this->head->next = nullptr;
        this->head->s = std::make_unique<Student>(s);
        this->tail = &*head; // Get a raw pointer
    }
    else {
        this->tail->next = std::make_unique<Node>();
        this->tail = &*this->tail->next; // Get a raw pointer
        this->tail->next = nullptr;
        this->tail->s = std::make_unique<Student>(s);
    }
}

而不是使用std::unique_ptr,另一个选项是简单地使Student成为Node类型的数据成员。但是,该决定可能表明不同的意图并具有不同的含义。例如,如果要将Student对象的所有权从Node对象传输到其他地方,则应使用std::unique_ptr。如果将Student对象直接作为成员,则可以通过调用Student的移动构造器来实现类似的效果,但是某些语义仍然会有所不同。例如,将Student的指针无效。有关两种方法的更多比较,请参见https://stackoverflow.com/a/31724938/88887578。

如果学生对象要超过链接列表,则它不应该是他们的所有者,最好将非所有人指向这种对象。在这种情况下,不要分配新的学生对象,而是从其他地方进行指针:

void studentRoll::insertAtTail(const Student* s) {
    if (head.get() == nullptr) {
        this->head = std::make_unique<Node>();
        this->head->next = nullptr;
        this->head->s = s;
        this->tail = &*head;
    }
    else {
        this->tail->next = std::make_unique<Node>();
        this->tail = &*this->tail->next;
        this->tail->next = nullptr;
        this->tail->s = s;
    }
}

我不知道您的程序的上下文(例如,如果这是用于编写链接列表的学校练习(,但是在认真的代码中,您应该使用标准库的std::list而不是滚动自己的链接列表。但是,在许多情况下,std::vector(类似于动态生长的数组(比链接列表更合适。

此外,您不应在其构造函数中传递学生指针。

,而不是给出Node无参数的默认构造函数,然后分配其s成员。

最新更新