#c #pointers #segmentation-fault #nodes #destructor
#c #указатели #ошибка сегментации #узлы #деструктор
Вопрос:
Я пытаюсь выяснить, почему я получаю ошибку seg из моей реализации односвязного списка.
Я создаю объект типа Deque с именем dq1, компилятор вызывает для него деструктор после завершения программы — деструктор вызывает remove_front(), который обрабатывает некоторые move() для head. Я считаю, что в этом и заключается проблема, но я не могу понять, где именно она находится.
Информация об отладчике — Не знаю, что с этим делать?
#0 0x4013ea std::unique_ptr<Node, std::default_delete<Node> >::get(this=0x8) (/usr/include/c /6/bits/unique_ptr.h:305)
#1 0x401586 std::unique_ptr<Node, std::default_delete<Node> >::operator bool(this=0x8) (/usr/include/c /6/bits/unique_ptr.h:319)
#2 0x40140b std::operator!=<Node, std::default_delete<Node> >(std::unique_ptr<Node, std::default_delete<Node> > constamp;, decltype(nullptr))(__x=<error reading variable: Cannot access memory at address 0x8>) (/usr/include/c /6/bits/unique_ptr.h:670)
#3 0x401132 Deque::size(this=0x7fffffffe520) (Deque.cpp:75)
#4 0x4010f2 Deque::empty(this=0x7fffffffe520) (Deque.cpp:66)
#5 0x4016dd main() (/test.cpp:12)
Deque.cpp
#include "Deque.h"
#include <iostream>
#include <memory>
#include <utility>
#include <stdexcept>
using std::cout;
using std::endl;
using std::move;
Deque::~Deque()
{
while (!empty()) remove_front();
}
void Deque::insert_front(int a)
{
std::unique_ptr<Node> new_node;
new_node->val = move(a);
new_node->next = move(head); // head is wiped.
head = move(new_node); //head is init. with new_node val*/
}
int Deque::remove_front()
{
if (empty()) {throw std::runtime_error(std::string("Empty"));};
std::unique_ptr<Node> old;
int return_value = head->val;
old = move(head);
head = move(old->next);
delete amp;old;
return return_value;
}
bool Deque::empty() const
{
return (size() == 0);
}
int Deque::size() const
{
int size_val = 0;
const Node* p = head.get();
while ( p != NULL)
{
size_val ;
p = p->next.get();
}
return size_val;
}
test.cpp
#include <iostream>
#include "Deque.h"
using std::cout;
using std::endl;
int main()
{
Deque dq1;
return 0;
}
deque.h
#include "Node.h"
#include <memory>
class Deque{
public:
Deque() = default;
Deque(const Dequeamp;);
~Deque(); //must use constant space
Dequeamp; operator=(const Dequeamp;){return *this;};
void insert_front(int);
int remove_front();
bool empty() const;
private:
friend Node;
std::unique_ptr<Node> head ;
std::unique_ptr<Node> tail ;
};
Node.h
#include "Node.h"
std::ostreamamp; operator<<(std::ostreamamp; out, const Nodeamp; n) {
return out << amp;n << ": " << n.val << " -> " << n.next.get();
}
Комментарии:
1. Вы никогда не должны вызывать
delete
указатель, принадлежащийstd::unique_pointer
. Проверьте своюint Deque::remove_front()
реализацию в вашем Deque.cpp2. Тогда как деструктор освобождает память, используемую уникальным указателем?
3. @TigerCode en.cppreference.com/w/cpp/memory/unique_ptr
4. Спасибо @GMichael
when the managing unique_ptr object is assigned another pointer via operator= or reset().
5. К сожалению, я все еще сталкиваюсь с ошибкой seg после удаления
delete *old
строки.
Ответ №1:
У вас есть UB прямо здесь:
std::unique_ptr<Node> new_node;
new_node->val = move(a);
вы создаете новый указатель, который инициализирован по умолчанию (указывает на nullptr
), и разыменовываете его. Вы должны инициализировать его с помощью std::make_unique
, если у вас C 14 или более поздней версии, или просто инициализировать его с помощью new
:
std::unique_ptr<Node> new_node = std::make_unique<Node>(); // C 14 or later
std::unique_ptr<Node> new_node( new Node ); // pre C 14
В этой строке также есть проблема:
delete amp;old;
эта строка не имеет никакого смысла. Вы получаете адрес самого указателя, который создается как локальная переменная, и пытаетесь удалить его. Если вы пытались удалить данные, на которые old
указывает, что это неверно — весь смысл std::unique_ptr
в том, чтобы сделать это автоматически.
Этот участник:
std::unique_ptr<Node> tail ;
это неправильно по замыслу, хотя вы, похоже, не используете его в своем коде. Это предполагает, что у вас будет несколько std::unique_ptr
, указывающих на один и тот же объект. Но этот указатель предназначен для уникального владения.
Похоже, у вас также есть проблема в Deque::size()
, но, не видя источника, невозможно сказать, что там не так.
В вашем деструкторе вам не нужно ничего делать (хотя это не повредит, если другие методы будут реализованы должным образом) — std::unqiue_ptr
уничтожит все данные рекурсивно.
Комментарии:
1. Я забыл включить size() в сообщение — я включил его сейчас. Вот куда привел меня мой отладчик. @Slava
2. Я считаю, что это связано с тем, как я создал указатель узла P
3. @TigerCode
size()
выглядит нормально, кажется, что проблема с использованиемnullptr
ininsert_front()
нарушает его.4. Для хвоста — это должен быть наблюдатель, тогда, поскольку мы не можем заставить head = tail быть двумя уникальными ptr, правильно?
5. Хвост может быть необработанным указателем, в противном случае у вас возникнет проблема, так как
Node
он принадлежит одномуhead
или предыдущемуNode::next
, и у вас не может быть более одного владельца сstd::unque_ptr
. Также вы можете использоватьstd::shared_ptr
, но в этом случае это было бы излишним.