Утечка памяти в списке узлов указателя ученика в C ++ - PullRequest
0 голосов
/ 29 апреля 2018

У меня есть список узлов, в котором каждый узел содержит указатель, указывающий на переменную Student (это класс), и указатель, указывающий на следующий узел. Вот мой код для insertAtTail.

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);

Но я не могу удалить их, потому что мне нужны эти "Студенты". И есть указатели, указывающие на «Студенты».

Спасибо !!

Обновление: вот мой деструктор

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

1 Ответ

0 голосов
/ 29 апреля 2018

Может ли кто-нибудь помочь мне с этим? Я думаю, что есть некоторые проблемы:

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

и

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

Но я не могу удалить их, потому что мне нужны эти "Студенты". И есть указатели на «Студенты».

Эта проблема, вероятно, указывает на то, что вам следует перепроектировать вашу программу. В C ++ вы должны выразить семантику владения и уточнить, какие объекты владеют какими ресурсами и отвечают за их очистку. Семантика владения в C ++ выражается через различные типы указателей:

Если определенный, отдельный объект владеет некоторой кучей памяти, вместо использования необработанных указателей и new и delete напрямую, используйте std::unique_ptr. std::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/8887578 для большего сравнения двух подходов.

Если объекты учащихся переживают связанный список, он не должен быть их владельцем, и было бы лучше взять указатель на такой объект, не являющийся владельцем. В этом случае не размещайте новые объекты ученика, а берите указатель из другого места:

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 члена, вы должны передать на него указатель ученика в его конструкторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...