So i have a Linked list implementation of my own and it can successfully keep integers and call them when needed with overloaded [] operator but when it comes to storing a class in my linked list, it seems that i can't call the class appropriately (using the same [] operator).
Called functions and members of my Linked List;
#include <iostream>
#include <assert.h>
template<typename T>
struct node {
T data;
node<T>* next;
};
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
void insert(T value) {
last->next = new node<T>;
last = last->next;
last->data = value;
last->next = NULL;
if (isEmpty()) {
head = last;
}
lenght++;
}
node<T>* search(int indx) {
node<T>* current;
current = head;
int count=0;
while (current != NULL) {
if (count == indx) {
break;
}
current = current->next;
count++;
}
return current;
}
T& operator [](int indx) {
assert(indx >= lenght - 1);
T result;
result = search(indx)->data;
return result;
}
};
And here is the main function and the class that i try to store;
#include <iostream>
#include <fstream>
#include <string>
#include "VectemLibrary.h"
class word {
public:
std::string value;
int count;
word(std::string value, int count): value(value),count(count) {
}
word() {
value = "NOT ASSIGNED";
count = 0;
}
word(const word& w1) {
value = w1.value;
count = w1.count;
}
~word() {
std::cout << "Word Destroyed" << std::endl;
}
};
int main()
{
Vectem<word> wordContainer;
word newWord("hello", 1);
wordContainer.insert(newWord);
std::cout << wordContainer[0].value;
}
Visual studio gave me the expection with this message at the last line where i call the first member of linked list with [];
Exception thrown at 0x7A0CF3BE (ucrtbased.dll) in Top 10 words.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
I think that my lack of experience with pointers may have caused the problem but if you see something that i can't, Please enlighten me.
There are other problems with the code you posted as well (e.g. isEmpty() is not declared or defined), but I'll focus on the issue you explicitly mentioned.
In your operator:
T& operator [](int indx) {
assert(indx >= lenght - 1);
// You declare this variable on the stack
T result;
result = search(indx)->data;
// And then you return this variable by reference; this is not okay
return result;
}
As mentioned in my code comments (and by #Johnny Mopp in his comment to your post), you shouldn't (can't) return a reference or pointer to a variable declared within the returning function and constructed on the stack. Anything on the stack will be destroyed once the function call ends, so any returned pointers or references to such variables will be dangling references; using said pointers or references will result in undefined behavior.
So you don't want to return a reference to a stack-allocated variable like result; you want to return a reference to the data within the node itself (which is allocated on the heap by insert()), as it will still be a valid reference after the function returns:
return search(indx)->data;
There are several problems with your code, but the most important is that you are not initializing the head, last, or lenght members of Vectem at all. An Access Violation error at address 0xCCCCCCCC is a good indication that uninitialized memory is being accessed, as some compilers/setups fill uninitialized memory with 0xCC bytes, thus head and last are initially 0xCCCCCCCC in your case.
You need to add appropriate constructors to Vectem (as well as a destructor, a copy constructor, and a copy assignment operator, per the Rule of 3), eg:
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
Vectem() : head(NULL), last(NULL), lenght(0) {}
Vectem(const Vectem &src) : head(NULL), last(NULL), lenght(0)
{
// copy src's data to *this as needed ...
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
...
};
Or, in C++11 and later, you can initialize the members directly in their declarations (also, be sure to add a move constructor and a move assignment operator, per the Rule of 5), eg:
template<typename T>
class Vectem {
private:
node<T>* head = nullptr;
node<T>* last = nullptr;
int lenght = 0;
public:
Vectem() = default;
Vectem(const Vectem &src)
{
// copy src's data to *this as needed ...
}
Vectem(Vectem &&src) : head(src.head), last(src.last), lenght(src.lenght)
{
src.head = nullptr;
src.last = nullptr;
src.lenght = 0;
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
Vectem& operator=(Vectem &&rhs)
{
// clear *this as needed...
head = rhs.head; rhs.head = nullptr;
last = rhs.last; rhs.last = nullptr;
lenght = rhs.lenght; rhs.lenght = 0;
return *this;
}
...
};
That being said, insert() is also buggy, as it is dereferencing last before checking that last is actually pointing at a valid node. Try something more like this instead:
void insert(T value) {
node<T> *n = new node<T>{value, NULL};
if (!head) head = n;
if (last) last->next = n;
last = n;
++lenght;
}
Alternatively:
void insert(T value) {
node<T> **p = (last) ? &(last->next) : &head;
*p = new node<T>{value, NULL};
last = *p;
++lenght;
}
Related
i need to code a linked list for university in c++, mostly to practice coding iterators.
I tested it with some basic cases and it works but after i pass it in valgrind and the test server for the program i get a list of different errors. Maybe somebody can help me not to despair.
(At the end i will append the error list)
template <typename T = float>
class ForwardList
{
struct Node
{
/// Constructs a Node from a data value and a link to the next element.
Node(const T &data, Node *next) : data{data}, next{next} {}
/// A Node owns all nodes after it, so it deletes them on destruction
~Node() { delete next; }
//Performs a deep copy of the Node and all Nodes after it. Bad practice but we got it like that
Node *clone() const
{
if (next == nullptr)
{
return new Node{data, nullptr};
}
else
{
return new Node{data, next->clone()};
}
}
T data;
Node *next;
};
public:
ForwardList() : head(nullptr) {}
/// Copy constructor performs a deep copy of the other list's Nodes
ForwardList(const ForwardList &other)
{
head = other.head->clone();
}
/// Destructor makes sure that all Nodes are correctly destroyed
~ForwardList()
{
while (head->next != nullptr)
{
Node *tmp = head;
head = head->next;
delete tmp;
}
delete head;
}
/// Copy assignment operator uses the copy-and-swap idiom to make a safe
/// assignment
ForwardList &operator=(ForwardList other)
{
swap(*this, other);
return *this;
}
/// Add an element to the front of the list.
void push_front(const T &value)
{
std::cout << "Num: " << numberOfNodes << std::endl;
Node *item = new Node(value, nullptr);
if (head==nullptr)
{
head = item;
}else
{
item->next=head;
head = item;
}
numberOfNodes++;
}
/// Remove the first element of the list. Calling this function on an empty
/// list is undefined behavior. When implementing this function, be careful
/// to delete the one and only the one element that is removed.
void pop_front()
{
Node *item;
item = head->next;
delete head;
head = item;
numberOfNodes--;
}
/// Get a reference to the first element of the list
/// (const and non-const version)
T &front()
{
return head->data;
}
const T &front() const
{
return head->data;
}
/// Return true is the list is empty
bool empty() const
{
return numberOfNodes == 0 ? true : false;
}
std::size_t size() const
{
return numberOfNodes;
}
friend void swap(ForwardList &l, ForwardList &r)
{
Node *tmp = l.head;
l.head = r.head;
r.head = tmp;
}
private:
Node *head;
size_t numberOfNodes = 0;
};
And now the fun part (i will put it on pastebin because its pretty long):
https://pastebin.com/4JAKkJtP
Your issue is that ~Node tries to delete its next, and you also try to walk the list in ~ForwardList. By deleting ~Node(), you let ForwardList handle cleanup and everything works.
The clue here is that valgrind reported use after free, meaning something was deleting a pointer twice. That was a clue to look at everything that deletes a Node* (or really, delete in general).
I don't fully understand the concept of templates and am trying to get some help on how to implement one on my linked list below. I'm trying to get my code to be able to support the following types : List< List<std::string> > List<std::string> List<int>. I was wondering if there was any way someone could give me an example of how to convert these items into templates in addition to trying to explain what is happening? I'm new to c++ so any help I can get would be appreciated.
#include <string>
#include <iostream>
#include <cstddef>
using Item = std::string;
// TURN DList into a template!
class DList {
private:
class DListNode {
public:
Item item;
DListNode * next;
DListNode * prev;
DListNode(Item i, DListNode *n=nullptr, DListNode *p=nullptr) {
item = i;
next = n;
prev = p;
}
};
DListNode * head;
DListNode * tail;
public:
class iterator {
DListNode *node;
public:
iterator(DListNode *n = nullptr) {
node = n;
}
Item& getItem() { return node->item; }
void next() { node = node->next; }
void prev() { node = node->prev; }
bool end() { return node==nullptr; }
friend class DList;
};
public:
DList() {
// list is empty
head = nullptr;
tail = nullptr;
}
bool empty() {
return head==nullptr;
}
void append(Item a) {
DListNode *node = new DListNode(a,nullptr,tail);
if ( head == nullptr ) {
// empty list
head = node;
tail = node;
} else {
tail->next = node;
tail = node;
}
}
void insertAfter(iterator it, Item item)
{
if(head == nullptr || it.node == nullptr) { // NULL iterator means insert at head
DListNode *node = new DListNode(item,head); // next=head, prev=NULL
if ( head == nullptr) // same as zyBook
head = tail = node;
else { // if inserting before head, it.node==NULL
head->prev = node;
head = node;
}
} else if (it.node == tail) {
DListNode *node = new DListNode(item,nullptr,tail); // next=NULL, prev=old tail
tail->next = node;
tail = node;
} else {
DListNode *node = new DListNode(item,it.node->next,it.node);
it.node->next = node;
node->next->prev = node;
}
}
void erase (iterator it) {
DListNode *succ = it.node->next; // successor node
DListNode *pred = it.node->prev; // predecessor node
if (succ != NULL)
succ->prev = pred;
if (pred != NULL)
pred->next = succ;
if (it.node == head)
head = succ; // head is following node
if (it.node == tail)
tail = pred; // tail is previous node
delete it.node; // delete the node; not shown in zyBook, but necessary in C/C++
// iterator is now invalid, caller should not use it again
}
iterator begin() {
return iterator(head);
}
iterator reverse_begin() {
return iterator(tail);
}
};
template <typename Item>
std::ostream& operator << (std::ostream& out, DList<Item> &l)
{
out << "{";
auto it = l.begin();
out << it.getItem();
it.next();
for(; !it.end(); it.next())
{
out << ", " << it.getItem();
}
out << "}" << std::endl;
return out;
}
int main()
{
{
DList<std::string> l;
l.append("eggs");
l.append("milk");
l.append("bread");
std::cout << l;
}
{
DList<int> l;
l.append(1);
l.append(2);
l.append(3);
std::cout << l;
}
return 0;
}
Actually, you almost have all you need, but you are still using a regualar class with a concrete type.
using Item = std::string;
class DList { ... };
So first we drop the concrete type:
// using Item = std::string;
class DList { ... }; // sure Item is now undefined...
Then we tell the class to be a template
template <typename Item>
class DList { ... };
Now Item got re-introduced, but instead of being a concrete type, it's now a generic one. That's it, you have a template list (assuming the list is implemented correctly, I didn't check).
Whenever you now instantiate your list:
DList<int>;
DList<std::string>;
// ...
You create a totally new, independent data type (which means especially, that you cannot assign a DList<int> to a pointer to DList<double>, just all alike as you cannot assign a int to a pointer to double either).
When you instantiate a template, every occurence of a template parameter will be replaced with the type you instantiated the template with, e. g. in DList<int>, every occurence of Item will be replaced with int.
Well, all this is just a very short introduction, there's quite a lot to follow yet, but that's rather to be handled in book than in an answer on stackoverflow...
Some notes to your node's constructor, though:
DListNode(Item i /* , ... */) { item = i; }
At very first, you should get used to using constructor's initialiser list (not to be confused with std::initializer_list):
DListNode(Item i /* , ... */) : item(i) { }
You avoid default initiasation + assignment in favour of direct initialisation by value. Additionally, some types (non-default constructible ones, const members and references) only can be initialised that way.
Then you are producing an unnecessary copy:
DListNode(Item i /* , ... */) : item(i) { }
// ^ temporary copy ^ final copy, created from temporary
You avoid that copy, if you accept the item by reference:
DListNode(Item const& i /* , ... */) : item(i) { }
// now copies from reference, one copy less
You can additionally provide move semantics:
DListNode(Item&& i /* , ... */) : item(std::move(i)) { }
so that objects you don't need outside the list any more can be moved into (well, actually their contents). In some cases, this can be much cheaper than a full copy...
All said about the constructor (apart from the initialiser list) applies to the append and insertAfter functions as well.
Initialiser lists and avoiding copies is general advice, unrelated to templates...
I am writing an Ordered Linked List class definition (OLList). I have written the assignment operator function, but when I try to test it by chaining assignment operations, the program gets caught in the while loop of the OLList::copy function. I know this because I tested using console prints.
//OLList.h
struct Node {
ListItem item;
Node *next;
};
class OLList {
public:
OLList& OLList::operator =(const OLList& rhs)
{
if (this != &rhs) {
destroy();
copy(rhs);
}
return *this;
}
void OLList::destroy()
{
Node *current_node = this->headM;
Node *next_node;
while(current_node->next != nullptr)
{
next_node = current_node->next;
delete(current_node);
current_node = next_node;
}
return;
}
void OLList::copy(const OLList& source)
{
Node *new_node, *current_node;
Node *current_source_node = source.headM;
this->headM->item = source.headM->item;
current_node = this->headM;
while(current_source_node->next != nullptr)
{
new_node = new(Node);
current_node->next = new_node;
current_node = current_node->next;
current_source_node = current_source_node->next;
current_node->item = current_source_node->item;
}
return;
}
}
Below is the code used to test the class. I have made sure that the print() function works fine so that's definitely not an issue.
//main.cpp
int main()
{
OLList the_list;
the_list.insert(1);
the_list.insert(2);
OLList second_list;
second_list.insert(3);
second_list.insert(4);
OLList third_list;
third_list.insert(5);
third_list.insert(6);
third_list = second_list = the_list;
third_list.print();
}
When it is compiled and run, the program never terminates as it is caught in the loop mentioned above.
Your destroy() method will fail if headM is nullptr. You should be using while(current_node != nullptr) instead of while(current_node->next != nullptr). But more importantly, it doesn't reset headM to nullptr after destroying the list. So after operator= calls destroy(), headM is no longer in a valid state for copy() to use.
Your copy() method is similarly not checking if either source or target headM are nullptr. But more importantly, it assumes the target list is empty beforehand, otherwise it leaks memory, if it does not crash outright (per above). And frankly, it simply is not coded correctly in general to copy one list to another.
So, your code is invoking undefined behavior, this anything could happen.
Like #PaulMcKenzie stated in comments, you really should be using a proper copy constructor instead (and a destructor - and since you are clearly using C++11 or later, a move constructor and move assignment operator, too - see the Rule of 5). Your assignment operator can then be implemented using your copy constructor (and likewise for move assignment).
Try something more like this:
struct Node {
ListItem item;
Node *next = nullptr;
Node(const ListItem &value) : item(value) {}
};
class OLList {
private:
Node *headM = nullptr;
public:
OLList() = default;
OLList(const OLList &src)
{
Node *current_source_node = src.headM;
Node **current_node = &headM;
while (current_source_node)
{
*current_node = new Node(current_source_node->item);
current_node = &((*current_node)->next);
current_source_node = current_source_node->next;
}
/* alternatively:
Node *current_source_node = src.headM;
while (current_source_node) {
insert(current_source_node->item);
}
*/
}
OLList(OLList&& src)
{
src.swap(*this);
}
~OLList()
{
Node *next_node;
while (headM)
{
next_node = headM->next;
delete headM;
headM = next_node;
}
}
void clear() {
OLList().swap(*this);
}
OLList& operator=(const OLList& rhs)
{
if (this != &rhs) {
OLList(rhs).swap(*this);
}
return *this;
}
OLList& OLList::operator=(OLList&& rhs)
{
OLList(std::move(rhs)).swap(*this);
return *this;
}
void swap(OLList &other) {
std::swap(headM, other.headM);
}
void insert(const ListItem &value) {
...
}
void print() const {
...
}
...
};
I am trying to implement a one-directional list. Everything works perfectly fine until the command m3=m1+m2 is doubled in the main function. When I was debugging I noticed that in the overloaded = operator, after the destruction happens, the values assigned to the o1 object disappear. No idea if there is something wrong with destructor, or the operator =.
Here is the code:
#include <iostream>
using namespace std;
template <class T>
class Element;
template <class T>
class List{
friend class Element<T>;
Element<T> *head;
public:
List(){
cout<<"konstruktor"<<endl;
head=NULL;
}
~List() {
Element<T> *tmp = head;
cout << "destruktor" << endl;
while (tmp) {
//tmp = tmp->next;
delete head;
head = tmp;
}
}
friend istream &operator>>(istream &p, List<T> &o1){
Element<T>* new_ele;
Element<T>* it;
it=o1.head;
new_ele=new Element<T>;
p>>new_ele->value;
new_ele->next=NULL;
if (o1.head==NULL){
o1.head=new_ele;
}
else{
while (it->next!=NULL){
it=it->next;
}
it->next=new_ele;
}
return p;
}
friend ostream &operator<<(ostream &s, List<T> &o1){
Element<T>* it;
it=o1.head;
while(it){
s<<it->value<<" ";
it=it->next;
}
return s;
}
List <T> &operator=(const List<T> &o1){
if (this==&o1){
return *this;
}
Element<T> *it1, *it2, *itc;
this->~List();//this is the where everything goes haywire
itc=head;
it1=o1.head;
while(it1){
itc=new Element<T>;
if (!head) head=it1;
itc->next=NULL;
itc->value=it1->value;
it1=it1->next;
itc=itc->next;
}
return *this;
}
List<T> &operator+(List<T> &o1){
if(o1.head==NULL){
return *this;
}else if(head==NULL){
return o1;
}
static List<T> res=*this;
Element<T> *it;
it=res.head;
while(it->next) {
it = it->next;
}
Element <T> *o1_it=o1.head;
while(o1_it){
Element<T> *copy;
copy=new Element<T>;
copy->next=NULL;
copy->value=o1_it->value;
it->next=copy;
it=it->next;
o1_it=o1_it->next;
}
return res;
}
int length_list(){
Element<T> *it;
int count_elements=0;
it=head;
while(it->next){
count_elements++;
it=it->next;
}
return count_elements;
}
void bubblesort_List(){
Element<T> *it;
for(int i=0; this->length_list() > i;i++){
it = head;
while (it->next) {
if (it->next->value < it->value) {
T tmp = it->value;
it->value = it->next->value;
it->next->value = tmp;
}
it = it->next;
}
}
}
};
template <class T>
class Element{
friend class List<T>;
friend istream &operator>>(istream &p, List<T> &o1);
friend ostream &operator<<(ostream &s, List<T> &o1);
Element<T> *next;
T value;
public:
Element(){
next=NULL;
}
};
int main(){
List<int> m1, m2, m3;
cin>>m1>>m1>>m1;
cin>>m2;
m3=m1+m2;
m3=m1+m2;
cout<<m3<<endl;
return 0;
}
this->~List();
this destroys the object. It doesn't just run the destructor.
After an object is destroyed, its storage remains, but there is no object there. Interacting with the storage as if there was an object there is undefined behavior.
Move the body of the destructor to a helper function called clear():
Fix it:
clear() {
std::cout << "clear" << std::endl;
while (head) {
Element<T>* tmp = head;
head=head->next;
delete tmp;
}
}
and make your ~List() be simply clear().
Next, lets fix your operator=.
List& operator=(List&&o) {
if (this==&o)
return *this;
clear();
head = o.head;
o.head = nullptr;
return *this
}
List& operator=(const List &o) {
if (this== &o)
return;
*this = List(o); // call operator=(List&&) using a copy of o
return *this;
}
List(List&& o):
head(o.head)
{
o.head = nullptr;
}
List():head(nullptr) {}
// all of the work goes on here:
List(List const& o):List()
{
Element<T>* src = o.head;
Element<T>** dest = &head;
while(src) {
*dest = new Element<T>;
(*dest)->value = src->value;
(*dest)->next = nullptr;
src = src->next;
dest = &((*dest)->next);
}
}
I have chained everything to work off simpler functions.
=(&&) and (&&) (move-assign and move-construct) copies head and clears the source. This is a small amount of duplicate code.
=(const&) (copy-assign) uses =(&&) (move-assign) and (const&) (copy-construct). The business of copying nodes is hard; do it in one situation. Copying into an empty instance is easier than into one that already exists.
(const&) is the only one that does the work. Here I keep a pointer-to-the-tail-pointer of my linked list, and a pointer-to-the-next-element-to-add.
Then I splice a copy of the next element in, and update my tail and next element pointers.
Two main issues
call delete to invoke destructor, never explicitly call ~List. Just call it free_list or something and call it on head
your destructor is broken, it's not freeing memory up correctly
you want :
void free_list (Element<T> * head) {
Element<T> *tmp = head;
while (tmp != null) {
head = tmp.next;
delete tmp;
tmp = head;
}
}
i made this linked list class in c++ and it works fine except after i run it the program goes unresponsive. i have located the line that's causing the problem but i have no idea why. Even when i type it differently it still does the same thing.
Here's my list class:
#include <string>
template<class T>
class List : public Object{
private:
Node<T>* first;
Node<T>* last;
int length;
public:
List() : Object(new std::string("List")) {
first = NULL;
last = NULL;
length = 0;
}
~List() {
delete first;
delete last;
}
void Add(T value) {
if(first==NULL)
first = new Node<T>(NULL, value);
else if(last==NULL)
---->last = new Node<T>(first, value);<-----
else
last = new Node<T>(last, value);
length++;
}
T Remove(T value) {
Node<T>* temp = first;
while(temp!=NULL) {
if(temp->GetValue()==value) {
temp->GetPrev()->SetNext(temp->GetNext());
temp->GetNext()->SetPrev(temp->GetPrev());
delete temp;
length--;
return value;
}
temp = temp->GetNext();
}
return 0;
}
T Get(int index) {
Node<T>* temp = first;
int i = 0;
while(temp!=NULL) {
if(i==index)
return temp->GetValue();
i++;
temp = temp->GetNext();
}
return 0;
}
};
when i remove the marked line above the program go unresponsive. This is my Node constructor:
#include <string>
template<class T>
class Node : public Object{
private:
Node* next;
Node* prev;
T value;
public:
Node(Node* prev, T value) : Object(new std::string("Node")){
if(prev!=NULL) {
prev->next = this;
this->prev = next;
}
next = NULL;
this->value = value;
}
~Node() {
delete next;
}
T GetValue() {
return value;
}
Node* GetNext() {
return next;
}
Node* GetPrev() {
return next;
}
};
my object class:
#include <string>
class Object {
private:
std::string* type;
public:
Object() {
type = new std::string("Object");
}
Object(std::string* type) {
this->type = type;
}
~Object() {
delete type;
}
std::string* GetType() {
return type;
}
};
my Test.cpp
#include <iostream>
#include <string>
#include "Object.h"
#include "Node.h"
#include "List.h"
using namespace std;
int main () {
List<int> l;
l.Add(5);
l.Add(93);
l.Add(17);
l.Add(7789);
l.Add(60);
cout << "node 4 is:" << l.Get(3) << endl;
return 0;
}
error image http://i50.tinypic.com/2mw5phi.png
thanks for reading and please help as soon as you can, comment if you need me to supply more info.
Edit: There are many problems with your program, but what might be causing your crash: Your Add-function does not work correctly. It should be something like this:
if(first==NULL) {
first = new Node<T>(NULL, value);
last = first;
} else {
last = new Node<T>(last, value);
}
length++;
Otherwise, it will not correctly insert the second element. Why? With your original code, after the first add, your last is still NULL because of the else. So on the second add, you set last to new Node<T>(NULL, value). Therefore, it will not assign the first element's next pointer. And your list will be inconsistent.
Apart from that, there are double-frees, unnecessary heap-allocation of the string field in your Object class, ownership issues etc. To give you just one more example: Your List destructor will cause a heap corruption due to a double free. Calling delete first will delete all nodes due to the delete next in Node's destructor, as long as the list is consistent. Then you call delete last, but that object was already freed. This will corrupt your program's memory management and can also cause a crash at program exit.
Does this function seem correct to you??
It says GetPrev, but its actually getting next.
Node* GetPrev() {
return next;
}
I found that if I comment out this line in the Node constructor the code compiles:
if (next != NULL) {
// next->next = this;
prev = next;
}
Edit 1:
I also realized that you were doing this in your Node class:
private:
Node* next;
Node* prev;
T value;
Since these objects are declared in the Node class, they are at this time incomplete types. I managed to replicate that problem down to a simple one like this:
template <class T>
struct S {
S* s = new S();
~S() { delete s; }
};
int main() {
S<int> s; // Segmentation fault (core dumped) ./test > .stdout
}
This causes a crash because S is an incomplete type within itself.
I'm getting the same segementation fault as I got in your code. I'm pretty sure it's because the pointers in the Node class are built upon incomplete types; and accessing the data from them is looking into memory that isn't yours, hence the crash.