В функции find(T item) я проверяю, являются ли введенные данные строкой, затем я обрабатываю их отдельно, а для других типов данных я обрабатываю их в противном случае, потому что я использую строковую функцию compare() для проверки соответствовать. Программа работает нормально, когда в связанном списке есть совпадающая строка, и возвращает узел и печатает данные узла, но когда совпадающая строка не найдена, тогда вместо возврата пустого узла tmp программы аварийно завершают работу и говорят: «Ошибка сегментации (дамп ядра )». Есть ли способ исправить это?
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
class Node {
public:
T data;
Node *next;
Node *prev;
Node(){};
};
template <typename T>
class LinkedList {
private:
Node<T> *head;
Node<T> *tail;
public:
LinkedList(){
head = NULL;
tail = NULL;
}
void insertHead(T item){
Node<T> *tmp = new Node<T>();
if (head == NULL){
head = tmp;
head->prev = NULL;
head->data = item;
head->next = NULL;
tail = tmp;
} else {
tmp->next = head;
tmp->data = item;
tmp->prev = NULL;
head->prev = tmp;
head = tmp;
}
}
void insertLast(T item){
Node<T> *tmp = new Node<T>();
tmp->data = item;
if (head == NULL){
head = tmp;
head->prev = NULL;
head->next = NULL;
tail = tmp;
} else {
tmp->prev = tail;
tail->next = tmp;
tmp->next = NULL;
tail = tmp;
}
}
Node<T>* find(T item){
Node<T> *tmp = head;
if (typeid(item) == typeid(string)){
string s, d;
s = item;
d = tmp->data;
while(tmp != NULL){
if (s.compare(d) == 0){
return tmp;
}
tmp = tmp->next;
d = tmp->data;
}
Node<string> *tmp = new Node<string>();
tmp->data = "";
return tmp;
} else {
while(tmp != NULL){
if (tmp->data == item){
return tmp;
}
tmp = tmp->next;
}
tmp = new Node<T>();
tmp->data = -1;
return tmp;
}
}
void print(){
Node<T> *tmp;
tmp = head;
while (tmp != NULL){
cout << tmp->data << "->";
tmp = tmp->next;
}
cout << endl;
}
};
int main(){
LinkedList<string> l;
l.insertHead("Abc");
l.insertHead("def");
l.insertHead("ghi jk");
Node<string>* tmp = new Node<string>();
tmp = l.find("ghi");
cout << tmp << endl;
l.print();
}
Проблема в этом цикле:
while(tmp != NULL){ // tmp is not null, good
if (s.compare(d) == 0){
return tmp;
}
tmp = tmp->next; // after this tmp MIGHT be null!
d = tmp->data; // possibly dereferencing null-pointer, bad!
}
Как только этот цикл достигает конца списка и не находит искомую строку, он разыменовывает нулевой указатель, и вы получаете неопределенное поведение, которое проявляется как ошибка сегментации.
В нестроковой версии вы сделали это правильно, и, насколько я вижу, вам вообще не нужны две версии.
s.compare(d) == 0
точно такой же, как s == d
, который идентичен item == tmp->data
.
Единственная разница будет заключаться в возвращаемом значении, где я предлагаю вернуть nullptr
, если элемент не был найден. Что, если список содержит значение ""
или -1
? Вы не сможете определить значение из списка, кроме «значения по умолчанию».
Проблема:
В строках:
s = item;
d = tmp->data;
while(tmp != NULL){
if (s.compare(d) == 0){
return tmp;
}
tmp = tmp->next;
d = tmp->data;
}
Когда tmp->next
становится NULL, вы принимаете NULL->data
вместо d = tmp->data;
, что приводит к ошибке сегментации.
Решение:
Замените код на этот:
s = item;
while(tmp != NULL){
d = tmp->data;
if (s.compare(d) == 0){
return tmp;
}
tmp = tmp->next;
}
Дополнительная информация:
s == d
. Собственно из-за этого и требуется специализация строк.nullptr
вместо NULL
.using namespace std;
(Подробнее здесь).tmp
в cout << tmp << endl;
печатает адрес tmp
, а не значение, которое оно хранит.tmp->data = -1; return tmp;
вызовет ошибку, если вы попытаетесь использовать шаблон с типом, который не может быть представлен в виде целого числа. Вместо этого вам, вероятно, следует использовать return NULL;
или return nullptr;
.Node<string>* tmp = new Node<string>(); tmp = l.find("ghi");
использовать дополнительное пространство, которое не освобождается. Вместо этого используйте Node<string>* tmp; tmp = l.find("ghi");
.Полный код:
#include <iostream>
using std::endl;
using std::string;
using std::cout;
template <typename T>
class Node {
public:
T data;
Node *next;
Node *prev;
Node(){};
};
template <typename T>
class LinkedList {
private:
Node<T> *head;
Node<T> *tail;
public:
LinkedList(){
head = nullptr;
tail = nullptr;
}
void insertHead(T item){
Node<T> *tmp = new Node<T>();
if (head == nullptr){
head = tmp;
head->prev = nullptr;
head->data = item;
head->next = nullptr;
tail = tmp;
} else {
tmp->next = head;
tmp->data = item;
tmp->prev = nullptr;
head->prev = tmp;
head = tmp;
}
}
void insertLast(T item){
Node<T> *tmp = new Node<T>();
tmp->data = item;
if (head == nullptr){
head = tmp;
head->prev = nullptr;
head->next = nullptr;
tail = tmp;
} else {
tmp->prev = tail;
tail->next = tmp;
tmp->next = nullptr;
tail = tmp;
}
}
Node<T>* find(T item){
Node<T> *tmp = head;
while(tmp != nullptr){
if (tmp->data == item){
return tmp;
}
tmp = tmp->next;
}
return nullptr;
}
void print(){
Node<T> *tmp;
tmp = head;
while (tmp != nullptr){
cout << tmp->data << "->";
tmp = tmp->next;
}
cout << endl;
}
};
int main(){
LinkedList<string> l;
l.insertHead("Abc");
l.insertHead("def");
l.insertHead("ghi jk");
Node<string>* tmp;
tmp = l.find("ghi");
cout << ((tmp == nullptr) ? "nullptr":tmp->data) << endl;
l.print();
}
Для начала функция-член find
может вызвать неопределенное поведение в самом начале.
Node<T>* find(T item){
Node<T> *tmp = head;
if (typeid(item) == typeid(string)){
string s, d;
s = item;
d = tmp->data;
^^^^^^^^^^^^^
//
потому что вообще head
может быть равно nullptr
.
В цикле while вы также не проверяете, равен ли указатель tmp
nullptr
.
tmp = tmp->next;
d = tmp->data;
Создание фиктивного узла
Node<string> *tmp = new Node<string>();
tmp->data = "";
return tmp;
это плохая идея и может привести к утечке памяти. Лучше возвращать нулевой указатель.
Это выделение узла в main
Node<string>* tmp = new Node<string>();
tmp = l.find("ghi");
производит утечку памяти.
Функция может быть объявлена и определена как минимум следующим образом
Node<T> * find( const T &item )
{
Node <T> *result = head;
while ( result && result->data != item ) result = result->next;
return result;
}
Более того, его можно перегрузить следующим образом
template <typename BinaryPredicate>
Node<T> * find( const T &item, BinaryPredicate binary_predicate )
{
Node <T> *result = head;
while ( result && not binary_predicate( result->data, item ) ) result = result->next;
return result;
}