Reputation: 61
I'm getting segmentation fault error with the follow main function. The header file has a linked list implementation with an iterator class and different testing members.
The thing is that the segmentation fault error shows up on a certain compilers and goes away on other. I tried using GDB to debug for any errors, but the program executes fine.
This is really bugging me as I cannot find the reason this would throw a segmentation fault error.
Any help is greatly appreciated.
EDIT: I noticed that with this updated main, the logic is flawed and does not insert the node at the specified location. So, I'll have to do some more debugging to figure out the issue.
It seems the else if
block inside insert()
is executed instead of the else
block.
Main.cpp
#include <iostream>
#include "List.h"
int main(){
List<double> l;
l.push_back(1.1);
l.push_back(2.2);
l.insert(l.begin()++, 3.3);
l.printList();
}
List.h
#include <cstdint>
#include <iostream>
#include <memory>
template<typename T>
class List
{
public:
class Node {
public:
Node(T value) : value_(value) {}
Node(T value, Node* prev, Node* next) : value_(value), prev_(prev), next_(next) {}
T value_;
Node* next_;
Node* prev_;
};
Node* head_;
Node* tail_;
//! An iterator over the list
class iterator
{
public:
Node* iNode;
iterator(Node* head): iNode(head){ }
~iterator() {}
T& operator*() {
return iNode -> value_;
}
iterator& operator++() {
iNode = iNode->next_;
return *this;
}
iterator operator++(int ignored) {
iNode = iNode->next_;
return *this;
}
iterator& operator--() {
iNode = iNode->prev_;
return *this;
}
//! Is this iterator pointing at the same place as another one?
bool operator== (const iterator& it) const {
return this->iNode == it.iNode;
}
//! Is this iterator pointing at a different place from another?
bool operator!= (const iterator& it) const {
return this->iNode != it.iNode;
}
};
//! Default constructor
List() :tail_(nullptr) {}
void push_back_node(T value)
{
Node* newnode = new Node(value, tail_, nullptr); //make and link new tail node
if (tail_)
{
tail_->next_ = newnode; // link in new node
}
else
{
head_ = newnode;
}
tail_ = newnode; // update tail
}
//! Copy constructor
List(const List& lst) : head_(nullptr), tail_(nullptr)
{
Node* cur = lst.head_; //get first source item.
while (cur) // if there is a source item to copy
{
push_back_node(cur->value_); // stick the item on the end of this list
cur = cur->next_; // get next source item
}
}
void clear()
{
while (head_)
{
delete std::exchange(head_, head_->next_);
}
tail_ = head_;
}
//! Copy assignment operator
List& operator= (const List& list_copy) {
List tmp(list_copy);
clear();
std::swap(*this, tmp);
return *this;
}
//! Move constructor
List(List&& move) {
head_ = std::move(move.head_);
tail_ = std::move(move.tail_);
}
////! Move assignment operator
List& operator= (List&& list) {
head_ = std::move(list.head_);
tail_ = std::move(list.tail_);
return *this;
}
//! Destructor
~List() {}
//
// Accessors:
//
//! How many elements are in this list?
size_t size() const {
size_t size = 0;
auto temp = head_;
while (temp != nullptr)
{
size++;
temp = temp->next_;
}
return size;
}
//! Is this list empty?
bool empty() const {
return tail_ == nullptr && head_ == nullptr;
}
//! Get an iterator to the beginning of the list
iterator begin() {
return List<T>::iterator(head_);
}
//! Get an iterator just past the end of the list
iterator end() {
return List<T>::iterator(tail_);
}
//
// Mutators:
//
//! Copy an element to the front of the list
void push_front(const T& value) {
Node* node = new Node(value);
if (head_ == nullptr) {
head_ = node;
}
else {
node->next_ = head_;
head_ = node;
}
}
//! Move an element to the front of the list
void push_front(T&& value) {
Node* node = new Node(value);
if (head_ == nullptr) {
head_ = node;
}
else {
node->next_ = head_;
head_ = node;
}
}
//! Copy an element to the back of the list
void push_back(const T& value) {
Node* node = new Node(value);
if (tail_) {
tail_->next_ = node;
tail_ = tail_->next_;
}
else {
head_ = node;
tail_ = head_;
}
}
//! Add an element to the back of the list
void push_back(T&& value) {
Node* node = new Node(value);
if (tail_) {
tail_->next_ = node;
tail_ = tail_->next_;
}
else {
head_ = node;
tail_ = head_;
}
}
iterator insert(iterator position, const T& value) {
Node* newNode = new Node(value);
if (position == List<T>::iterator(head_)) {
newNode->next_ = head_;
head_ = newNode;
}
else if (!position.iNode->next_) {
position.iNode->next_ = newNode;
}
else {
Node* curr = head_;
while (curr->next_ != position.iNode)
{
curr = curr->next_;
}
newNode->next_ = curr->next_;
curr->next_ = newNode;
}
return position;
}
void printList() const{
auto node = head_;
while (node != nullptr)
{
std::cout << node -> value_ << " ";
node = node->next_;
}
}
iterator insert(iterator position, T&& value){
Node* newNode = new Node(value);
if (position == List<T>::iterator(head_)) {
newNode->next_ = head_;
head_ = newNode;
}
else if (!position.iNode->next_) {
position.iNode->next_ = newNode;
}
else {
Node* curr = head_;
while (curr->next_ != position.iNode)
{
curr = curr->next_;
}
newNode->next_ = curr->next_;
curr->next_ = newNode;
}
return position;
}
//! Remove an element from an arbitrary location
void erase(iterator it) {
Node* temp = it.iNode->next_;
// Copy data of the next node to current node
it.iNode->value_ = it.iNode->next_->value_;
// Perform conventional deletion
it.iNode->next_ = it.iNode->next_->next_;
free(temp);
}
};
Upvotes: 0
Views: 53
Reputation: 33931
Node(T value) : value_(value) {}
does not point prev_
or next_
anywhere useful, so
Node* node = new Node(value);
produces a node that is a timebomb unless you later set its prev_
and next_
members to point somewhere safe. push_back
doesn't do this.
Solution:
Use the
Node(T value, Node* prev, Node* next) : value_(value), prev_(prev), next_(next) {}
instead:
Node* node = new Node(value, nullptr, nullptr);
You will find a similar defects in all of the variants of push_back
, push_front
, and insert
. You can also use the Node(T value, Node* prev, Node* next)
constructor to simplify some of the work adding, pushing and inserting nodes by using the surrounding nodes in place of the nullptr
s
Upvotes: 1