утечка памяти в древовидной структуре с указателями и вложенным классом Node - PullRequest
0 голосов
/ 10 ноября 2019

У меня есть это назначение, в котором я должен определить класс Tree, который имеет вложенный частный класс Node.
Проблема в том, что я не могу использовать ни smart pointers, ни std::vector, ни оператор копирования и назначение копирования. И все, что мне разрешено использовать - это необработанные указатели.
Итак, я написал класс и протестировал его с valgrind , чтобы проверить, есть ли у меня утечка памяти, и я это сделал. Я знаю, что утечка памяти может происходить из класса Node, потому что я не free _children, но когда я это делаю, я получаю ошибку сегментации.
Я действительно не знаю, как решить эту проблему.

Спасибо заранее.

Ответы [ 2 ]

1 голос
/ 10 ноября 2019
T** getChildren() { return &this->children; }

возвращает адрес члена children. Позже вы используете этот адрес посредством разыменования через индексацию массива, выполняя это:

_info->getChildren()[index] = childTree;

Это вызывает неопределенное поведение . Чтобы решить эту проблему:

Измените ваш член на:

Tree** _children;

Измените свой ctor на:

Node(T data) 
    : _data( std::move(data) )
    , _children(new Tree<T,N>*[N]())
    , _isWord(false)
{
}

и обратите внимание на массив синтаксиса указателейа также инициализация значений элементов, которая будет заполнять нулем массив.

Наконец, измените элемент getChildren() на просто:

Tree** getChildren() { return this->_children; }

Это должно облегчить вашUB, и с этим твоя вина. Не собираюсь приукрашивать это. Ручное управление памятью в этом подвержено ошибкам и проблематично. Вам было бы гораздо лучше использовать хотя бы умные указатели, если не прямые бетонные объекты, где это оправдано. Но это то, что есть.

Альтернатива

Потерять динамическое распределение children полностью. Его размер уже зафиксирован спецификацией времени компиляции N. Так что используйте это.

Член становится просто:

Tree* _children[N];

Ctor все еще может инициализировать значение, выполнив это:

Node(T data) 
    : _data( std::move(data) )
    , _children()
    , _isWord(false)
{
}

И полностью полностью удалив delete [] children; издеструктор;

0 голосов
/ 10 ноября 2019

ну, это не код очистки с множеством способов утечки памяти. Вот, я думаю, у вас проблемы с:

Tree() : _info(nullptr) {} 
...
~Tree() {
    delete _info;
  }

Но, конечно, есть и другие места, где все может пойти не так:

Tree& operator= (Tree&& t) // check _info not null before calling delete _info

На словах - ваш class Treeимеет два конструктора. Тот, который принимает значение типа T: Tree(T data). Этот конструктор создает new узел. Это конструктор, который вы используете для создания Tree в вашей функции main().

Есть еще один конструктор, который у вас есть: по умолчанию. Это конструктор, который вызывается при создании дочерних узлов из class Node: _children = new Tree<T,N>[N];

Этот конструктор по умолчанию Tree() : _info(nullptr) не создает новый узел _info.

Когда программа заканчивается и вызываются деструкторы - предпринимается попытка удалить nullptr.

Простое исправление - изменить деструктор дерева:

~Tree() {
    if (_info) delete _info;
  }

YouNode::_children - это массив деревьев, но когда вы вызываете getChildren, вы возвращаете массив указателей. Так что вызов ins скорее всего повредит память. Или это может работать случайно.

Лучше изменить getChildren, чтобы либо вернуть ссылку на элемент как: Tree& getChildren(int index) { return _children[index]; }

, либо изменить значение _children на Tree** _children; С инициализацией как _children = new Tree<T,N>*[N];

...