Generic doubly linked list copy constructor efficiency and linkage - c++

I am having an issue with implementing the following two constructors:
List(const List& other)
~List()
Originally, the copy constructor was written as:
for (auto current = other._head._next; current != &other._head; current = current->_prev){
push_back(static_cast<T*>(current));
}
The above is considered ineffective and inefficient. So, I am trying to re-write it like this:
for (auto ptr = other._head._next; ptr != &other._head; ptr = ptr->_next)
{
T* item = new T(*dynamic_cast<T*>(ptr)));
Link<T>* current = &_head;
Link<T>* previous = &_head;
current->_next = item;
previous = current;
/*
// link to new head
_head._next = other._head._next;
_head._prev = other._head._prev;
// Link prev och next to correct head
_head._next->_prev = &_head;
_head._prev->_next = &_head;
*/
}
However, I am a total newbie on understanding how this Next, Prev, Next Prev, and finally connect together has to be done properly in the context of this copy constructor and the destructor. So, I am not sure why the above does not work and I am asking if someone know this and can help out here.
Link.hpp
template<class T>
class Link {
template<class> friend class List;
Link* _next, * _prev;
public:
Link() : _next(this), _prev(this) {}
virtual ~Link() {}
T* next() const { return dynamic_cast<T*>(_next); }
T* prev() const { return dynamic_cast<T*>(_prev); }
T* insert_after(T* in)
{
in->_next = this;
in->_prev = m_prev;
_prev->_next = in;
_prev = in;
return in;
}
T* insert_before(T* in)
{
return _prev->insert_after(in);
}
T* remove()
{
_prev->_next = _next;
_next->_prev = _prev;
return dynamic_cast<T*>(this);
}
void splice_after(Link* f, Link* l)
{
if (f != l){
f->_prev->_next = l->_next;
last->_next->_prev = f->_prev;
f->_prev = this;
l->_next = this->_next;
_next->_prev = l;
_next = f;
}
}
void splice_before(Link* f, Link* l)
{
m_prev->splice_after(f, l);
}
T* erase_first()
{
Link* tmp = _next;
_next = _next->_next;
_next->_prev = this;
return static_cast<T*>(tmp);
}
T* erase_last() {
Link* tmp = _prev;
_prev = _prev->_prev;
_prev->_next = this;
return static_cast<T*>(tmp);
}
};
List.hpp:
#include <string.h>
template<class T>
class List {
template<class X> class ListIter {
template<class> friend class List;
Link<T>* _ptr;
public:
// Typedefs
typedef std::bidirectional_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
typedef std::remove_const_t<X> value_type;
typedef X* pointer;
typedef X& reference;
public:
ListIter() : _ptr{ nullptr } {}
ListIter(Link<T>* ptr) : _ptr{ ptr } {}
ListIter(const ListIter& other) : _ptr{other._ptr} {}
ListIter& operator=(const ListIter& other)
{
_ptr = other._ptr;
return *this;
}
X& operator*() { return *dynamic_cast<T*>(_ptr); }
X* operator->() { return dynamic_cast<T*>(_ptr); }
ListIter& operator++() { _ptr = static_cast<T*>(_ptr->_next); return *this; }
ListIter& operator--(){ _ptr = static_cast<T*>(_ptr->_prev); return *this; }
ListIter operator++(int) { auto r(*this); operator++(); return r; }
ListIter operator--(int){ auto r(*this); operator--(); return r; }
difference_type operator-(const ListIter& other) {
return (_ptr - other._ptr);
}
friend auto operator<=>(const ListIter& lhs, const ListIter& rhs)
{
if ((lhs._ptr - rhs._ptr) < 0)
return std::strong_ordering::less;
else if ((lhs._ptr - rhs._ptr) > 0)
return std::strong_ordering::greater;
return std::strong_ordering::equivalent;
}
friend bool operator==(const ListIter& lhs, const ListIter& rhs)
{
return (lhs <=> rhs) == 0;
}
};
Link<T> _head;
public:
using iterator = ListIter<T>;
using const_iterator = ListIter<const T>;
List()
{
_head._next = &_head;
_head._prev = &_head;
}
List(const List& other) // Problem here, does not work, not sure how to get this solved.
{
for (auto ptr = other._head._next; ptr != &other._head; ptr = ptr->_next)
{
T* item = new T(*dynamic_cast<T*>(ptr)));
Link<T>* current = &_head;
Link<T>* previous = &_head;
current->_next = item;
previous = current;
/*
// link to new head
_head._next = other._head._next;
_head._prev = other._head._prev;
// Link prev och next to correct head
_head._next->_prev = &_head;
_head._prev->_next = &_head;
*/
}
}
List(const char* other)
{
for (size_t i = 0; i < strlen(other); ++i)
_head.insert_after(new T(other[i]));
}
~List()
{
while (_head._next != &_head)
{
pop_front(); // This isn't efficient.
}
}
T* pop_front()
{
return _head.erase_first();
}
T* pop_back()
{
return _head.erase_last();
}
void push_front(T* node) { _head.insert_before(node);}
void push_back(T* node) { _head.insert_after(node);}
};

First off: I think the design here is a terrible idea; it looks like you're using the curiously recurring template pattern, but relying on dynamic_cast is pointless if so (the whole point is to get compile-time polymorphism, which means a static_cast from Link<T> to T (which is a child of Link<T>) should always be safe, and if it's not (because maybe your _head is a placeholder of type Link<T>, not an instance of T?), it's because you've written code that incorrectly treats them equivalently. I might suggest a refactor into three components:
T - the user's chosen type, which need not inherit from any given class, where currently your use of the curiously recurring template pattern requires it to inherit from Link<T>
Link<T> - The platonic ideal of a forward and reverse linked element of a linked list with no associated value (used solely for _head), where _prev and _next are pointers to Link<T> that, aside from pointers to _head, always point to Node<T>s.
Node<T> - A child class of Link<T> that adds a T data member and very little else (to avoid overhead, almost all behaviors not related to T itself would be defined non-virtually on Link<T>)
With the three of those, plus appropriate emplace* methods, you have the same features you currently have:
_head can be a plain Link<T> (no need to store a dummy T, nor require uses to define a default constructor)
All other elements are Node<T>, and have the same overhead of your current Link<T> (one instance of T plus two pointers)
T can be of arbitrary type (emplace* methods means the construction of a T can be deferred until the moment the Node<T> is created, no copies or moves of T needed)
where #3 is a stealth improvement which I'll repeat:
T can be of arbitrary type, not just subclasses of Link<T>
and you get the added benefit of:
Hiding more of your implementation (Link and Node can be private helper classes, where right now Link must be part of your public API), avoiding a lot of back-compat constraints that come with more of the API publicly visible
Secondly, your code is perfectly effective, it's just mildly inefficient (setting four pointers via indirection per new element, when you could set just two per element, and two more at the end to establish the List invariant). It's still a O(n) copy operation, not the O(n**2) operation a Schlemiel the Painter's algorithm would involve.
Beyond that, taking your word that everything else works, all you need to do to write a maximally efficient copy constructor is:
Begin with a pointer to the current element (_head), which I'll call current_tail (because at every stage of the copy, it's the logical tail of the list to date, and if no other element is found, it will be the real tail)
For each new element copied from other:
Set its _prev to current_tail (because current_tail is the current tail of the List, creating the reverse linkage)
Set current_tail's _next to the new element (creating the forward linkage)
Set current_tail to the new element (because now that they're linked to each other completely; we don't need a previous at all)
When you've copied everything per step 2, fix up the cycle, tying the final element to _head and vice-versa.
The end result is simpler than what you were writing (because you don't need a previous pointer at all):
List(const List& other) // Problem here, does not work, not sure how to get this solved.
{
Link<T>* current_tail = &_head; // 1. The tail of an empty list points to _head
for (auto ptr = other._head._next; ptr != &other._head; ptr = ptr->_next)
{
T* item = new T(*dynamic_cast<T*>(ptr)));
// We have validly forward linked list at each loop, so adding new element just means:
item->_prev = current_tail ; // 2.1. Telling it the prior tail comes before it
current_tail->_next = item; // 2.2. Telling the prior tail the new item comes after it
current_tail = item; // 2.3. Update notion of current tail
}
current_tail->_next = &_head; // 3. Real tail's _next points to _head to indicate end of list
_head._prev = current_tail; // _head._prev is logical pointer to tail element, fix it up
}
You might need a couple casts sprinkled in there to deal the weirdness of List<T> also being T (and storing it with different types in different places), but otherwise that's it).
Voila, the only two uses of indirect variables per loop (both writes; all loads come from locals) rather than five (four written, one read; each reference to _prev is implicitly indirect use of this->_prev) involved when you push_back repeatedly.
For bonus points, write yourself a swap helper (using the copy-and-swap idiom), which really only needs to change four items (_head's _next and _prev to swap all the nodes of each List, the _next of the tail element, and the _prev of the current head element to point to the _head of the new owning List), and about six lines of simple boilerplate will get you a cheap, efficient and safe move constructor and assignment operator.

Related

Template demands Type and compiles anyway

I have been searching on stack overflow, in my textbook, and google and everywhere, for like 2 days now for to try and fix/understand this issue.
I am writing this code out of a book I bought and it looks right to me, and I have gotten the code to compile after misspelling 'appnd' and 'prepnd' on purpose, and using GNU compiler 10.2.1, but wanted to know why basically and see if I could make it also run on clang++ and not have the spelling error and all that.
Like I said, this code compiles on my computer with GNU, but also on compiler explorer it says there is an issue with the same compiler(the same issue).
So this error keeps coming up saying I cant make an append or prepend with no type when dude(Dmytro Kedyk) is using a template as the type.
And I know there is an std::append or whatever for strings, but i am not using std, so like what gives.
And if you look at the code he uses append before the line throwing the error, but it has no issue with append there.
I seems like it should work, and if I misspell 'append or prepend' it works on GNU. why is clang not playing nice, or am I not playing nice with clang?
error: ISO C++ forbids declaration of ‘append’ with no type [-fpermissive]
On this line
template<typename ARGUMENT> append(ARGUMENT const& a){
On the second line with the append I keep getting an error
Notes: This is not my code! This is just from a book I own.
transcribed with small changes and added notes from:
page 44 "Implementing Useful Algorithms in C++", by: Dmytro Kedyk
template <typename ITEM> class simpleDoublyLinkedList{ //template for item and class dec
struct Node{
ITEM item; //data
Node *next, *prev; //links to next and last
template<typename ARGUMENT> //tempalting for argument
Node(ARGUMENT const& a): item(a), next(0), prev(0) {} //creating node obj inside Node struct
} *root, *last; //pointers to next and last
void cut(Node* n){ //funct to unlink node, takes node *
assert(n); //assert on node object
(n == last ? last : n->next->prev) = n->prev; //
(n == root ? root : n->prev->next) = n->next; //
}
public:
simpleDoublyLinkedList(): root(0), last(0) {} //
template<typename ARGUMENT> append(ARGUMENT const& a){ //line throwing error
Node* n = new Node(a); //making new node 'with new'
n->prev = last; //add to end of list
if(last){last->next;} //move down list
last =n; //
if(root){root = n;} //if root is true make root n
}
class Iterator{
Node* current; //current node obj (like 'this' but not predefined)
public:
Iterator(Node* n): current(n){} // member taking node
typedef Node* Handle; //Handle pointer type made of node type
Handle getHandle(){return current;} //function to return current as handle
Iterator& operator++(){ //overloading ++ operator
assert(current);
current = current->next;
return* this;
};
Iterator& operator--(){ //overloading -- operator
assert(current);
assert(current);
current = current->prev;
return* this;
};
ITEM& operator* ()const{assert(current);return current->item;} //overloading *
ITEM& operator->()const{assert(current); return &current->item;} //overloading ->
bool operator==(Iterator const& rhs)const{return current == rhs.current;} //overloading ==
};
Iterator begin(){return Iterator(root);} //returing root from iterator
Iterator rBegin(){return Iterator(last);}
Iterator end(){return Iterator(0);}
Iterator rEnd(){return Iterator(0);}
void moveBefore(Iterator what, Iterator where){ //function to move items in list
assert(what != end()); //assert what is not end
if(what != where){ //omit self refrence
Node *n = what.getHandle(), *w = where.getHandle(); //
cut(n);
n->next = w;
if(w){
n->prev = w->prev;
w->prev = n;
}else{
n->prev = last;
last =n;
}
if(n->prev){n->prev->next = n;}
if(w == root){root= n;}
}
}
template<typename ARGUMENT> prepend(ARGUMENT const& a){ //other line throwing error
append(a);
moveBefore(rBegin(), begin());
}
void remove(Iterator what){
assert(what != end());
cut(what.getHandle());
delete what.getHandle();
}
simpleDoublyLinkedList (simpleDoublyLinkedList const& rhs){
for (Node* n = rhs.root; n; n=n->next){append(n->item);}}
simpleDoublyLinkedList &operator=(simpleDoublyLinkedList const&rhs){
return genericAssign(*this, rhs);}
~simpleDoublyLinkedList(){
while(root){
Node* toBeDeleted = root;
root = root->next;
delete toBeDeleted;
}
}
};
append and prepend are functions but he has not put a return type in front of them. I think those lines should have a void return type, like this
template<typename ARGUMENT> void append(ARGUMENT const& a){ //line throwing error
template<typename ARGUMENT> void prepend(ARGUMENT const& a){ //other line throwing error
Because they have no return type, the compiler is attempting to interpret the function name as the return type. It has not seen that type before (because it's not a type) so it complains that the "type" is undefined. (Well technically, if it actually does build, I guess it assumes a void return type and lets you get away with it but warns you)

C++ Nested Iterator Class (In Linked List Class) Insert_After Function

I have an iterator class nested in a LinkedList class. My question is how do I make the insert_after function using iterators. The rest of the code is given for information purposes, but the function I'm trying to get working is at the end.
Insert_After takes a position and inserts a value after it.
template <typename T>
class LinkedList : public LinkedListInterface<T> {
private:
struct Node {
T data; // data can be any type
Node* next; // points to the next Node in the list
Node(const T& d, Node* n) : data(d), next(n) {}
};
Node* head; // Is a pointer
class Iterator
{
private:
Node* iNode;
public:
Iterator(Node* head) : iNode(head){ }
~Iterator() {}
bool operator!=(const Iterator& rhs) const { return iNode != rhs.iNode; }
Iterator& operator++() { iNode = iNode->next; return *this; }
T& operator*() const { return iNode->data; }
};
/** Return iterator pointing to the first value in linked list */
Iterator begin(void) {
return LinkedList<T>::Iterator(head);
}
/** Return iterator pointing to something not in linked list */
Iterator end(void) {
return LinkedList<T>::Iterator(NULL);
}
/** Return iterator pointing found value in linked list */
Iterator find(Iterator first, Iterator last, const T& value) {
Iterator current = first;
bool found = false;
while (current != last) {
if (*current == value) {
return current;
}
++current;
}
return last;
}
Iterator insert_after(Iterator position, const T& value)
{
// Need help here
}
What I've tried so far resulted in a few errors.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
The error I got was Error C2664 'function' : cannot convert argument n from 'type1' to 'type2' for the line
Node* newNode = new Node(value, position);
Compiler Error C2819 type 'type' does not have an overloaded member 'operator ->' for line
previous->next = newNode;
I understand the errors but I'm not sure how to work around them.
I think the short answer to your compiler errors is that you are likely supposed to pass a Node or Node* as your second argument and not an iterator. previous is also an iterator, and therefore does not have a next call.
Long answer below about generally fixing the function in question:
There's a lot [not] going on in that function, which gets me wondering about the rest of the linked list class as well. I've only looked at this function, as it's the one you claim is causing you trouble.
SUBJECTIVE THOUGHT I generally hate working with iterators in my class functions. Deal with the Nodes directly as much as possible. The iterator pattern exists for universal traversal of containers no matter how they're laid out, and that abstraction makes it a pain to deal with inside your class.
Iterator insert_after(Iterator position, const T& value)
{
// Need to insert after position
Iterator previous = position;
++position;
Node* newNode = new Node(value, position);
previous->next = newNode;
}
As it currently stands, if position is anywhere but the last element, you will break your list and leak memory. This is because you never check what is after position. That first mistake leads into the second. newNode->next never gets set. Maybe it's default constructed to nullptr, that's fine. But if I'm inserting into the middle of my list, I need to connect newNode to whatever came after position originally.
Another question that you need to consider is "what if this is called on an empty list?" Does your begin() function handle that? Is it supposed to throw?
Iterator insert_after(Iterator position, const T& value)
{
Node* pos = position.iNode;
if (!pos) { // assumes an empty list if this is true
// Correctly build first Node of your list and get everything assigned
// that you can
// return the new iterator;
// or just throw
}
if (!pos->next) { // position at end of list if true
pos->next = new Node(value, position); // Is that second argument
// supposed to be an iterator?
return Iterator(pos->next);
} else {
// Some of this is probably redundant depending on how you are actually
// building Nodes, but the gist is it's important to ensure the list is
// not broken; connecting tmp before changing existing nodes helps the
// list stay intact for as long as possible
Node* tmp = new Node(value, position);
tmp->next = pos->next;
pos->next = tmp;
return Iterator(tmp);
}
A doubly linked list never seems attractive at first blush to a student, but it makes certain operations like erasing from the middle of the list so much easier. Yes, you have one extra pointer to deal with, but it makes it harder to lose Nodes as well. Along with bi-directional iteration.

C++: Copy constructor crashing

I am having trouble coding Copy constructor for C++ HashTable. Now below is the class structure
template <class TYPE>
class HashTable : public Table<TYPE>
{
struct Record
{
TYPE data_;
string key_;
Record* Next;
Record(const string& key, const TYPE& data)
{
key_ = key;
data_ = data;
Next = nullptr;
}
Record(const Record& a) {
if(!a.key_.empty()){
if(a.Next == nullptr){
Next = nullptr;
}
else
{
Record* temp = a.Next ;
Record *temp2 = Next;
while(temp != nullptr)
{
temp2 = temp ;
temp = temp->Next ;
}
temp2->Next = nullptr;
}
data_ = a.data_ ;
key_ = a.data_ ;
} // user-
};
int TableSize;
Record** records;
}
};
and below is the copy constructor
template
HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
records = new Record*[other.TableSize];
TableSize = other.TableSize;
for(int i = 0 ; i < other.TableSize; i++)
records[i]= (new Record(*other.records[i]));
}
I have also posted the code on ideone http://ideone.com/PocMTD. The code for copy constructor seems to be crashing. I don't see any memory leak that will cause the program to crash. I have tried memcopy, using the insert function and the all seems to fail.
Replace int TableSize; and Record** records; with std::vector<std::unique_ptr<Record>>
In Record, change Record* Next; to Record* Next=nullptr;.
Stop calling new.
Include HashTable(HashTable&&)=default;.
HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
records.reserve( other.records.size() );
for (auto const& rec_in : other.records)
records.emplace_back( new Record(*rec_in) ); // make_shared<Record> in C++14
}
Now we are no longer doing manual memory management. So an entire set of worries is gone.
Next, look at that raw Next pointer. It is bad news. When you copy a Record, the Next pointer points into the old set of Record structures.
We can fix this in a few ways. The slickest is to use an offset pointer.
template<class T>
struct offset_ptr {
std::ptrdiff_t offset = std::numeric_limits<std::ptrdiff_t>::max();
explicit operator bool()const {
return offset!=std::numeric_limits<std::ptrdiff_t>::max();
}
T* get() const {
return (T*)( offset+(char*)this );
}
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
operator T*() const { return get(); }
offset_ptr(std::nullptr_t):offset_ptr() {}
explicit offset_ptr(T* p) {
if (!p) return;
offset = (char*)p-(char*)this;
Assert(*this);
}
offset_ptr()=default;
offset_ptr(offset_ptr const&)=default;
offset_ptr& operator=(offset_ptr const&)=default;
offset_ptr(offset_ptr&&)=default;
offset_ptr& operator=(offset_ptr&&)=default;
};
which instead of storing a pointer by absolute location, stores an offset.
Now we do this:
template<class TYPE> struct Table{};
template <class TYPE>
class HashTable :public Table<TYPE>
{
public:
struct Record
{
TYPE data_;
std::string key_;
offset_ptr<Record> Next;
Record(const std::string& key, const TYPE& data)
{
key_ = key;
data_ = data;
Next = nullptr;
}
Record(const Record& a)
{
if(!a.key_.empty())
{
if(a.Next == nullptr)
{
Next = nullptr;
}
else
{
auto temp = a.Next;
while(temp != nullptr)
{
Next = temp;
temp = temp->Next;
}
}
data_ = a.data_;
key_ = a.data_;
}
}
};
std::vector<Record> records;
};
and no copy ctor is needed; the offset ptr knows the location of the other record as an offset within the records. Data is stored by-value instead of by-reference.
Note that we have a vector of Records, not pointers-to-Records. This is key for the offset_ptr to work. Resizing isn't a problem, as the offsets remain the same. Copying remains safe, as offsets on each side now refer to other elements within their vector. Inserting/removing in the middle is dangerous, but simply nulling elements is not.
Note that buffers of size max std::ptrdiff_t or beyond are not supported by the above offset_ptr. On a 64 bit system that is about 2 gigs; on a 64 bit system it is large. (I don't use 0 for the null value, because if I did then an offset_ptr<X> as the first member of a struct X would nullify if I ever made it point to its enclosing X.)
boost also has a less bespoke offset_ptr type. The implementation above is meant as a sketch of how easy it is, not a solid implementation.
You do not show the complete code here (neither on ideone), but let me take a guess based on what I see.
I assume that your other object, which you pass in the copy c'tor has a fully set up list of Records.
I further assume that your HashTable class has a destructor (not shown) which deletes all the linked Records.
Your copy constructor calls the copy c'tor of Record(for each entry in the array of pointers to Record). The Record coyp c'tor only makes a shallow copy, i.e. only the pointer to the next element is copied (it will still point to the next element of the copied Record from the other hash table.
Thus, when other and its copy are deleted (at the end of scope or program; not shown), you will have double deletion (crash).
Fix: Make sure that Record has correct copy constructor, copy assignment and destructor (maybe even move c'tor and move assignment) (rule of five).
The same applies for the HashTable class as well.
Better fix: Use std::unordered_map.

Nodes added to linked list revert back to NULL after function

I'm reading lines from a file and storing them into linked list.
void add(llist *list, somevalue) {
Node *newnode = (Node *) malloc(sizeof(Node));
newnode->value = somevalue;
newnode->next = list->head;
list->head = newnode;
}
and I call this function from an initialize function which opens the file and reads lines from the file.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
And another problem that I realized is the values get concatenated every time I try to print the value. That is to say, the output would be:
First Loop: Tony
Second Loop: Peter
TonyPeter
Third Loop: Mir
PeterMir
TonyPeterMir
How do I fix it so the add function permanently adds the node to the linked list?
Why would the values be jumbled up like that?
----EDITED----
The list is a global variable, and here are some more snippets from the init function. This is the while loop with the problem:
//open file
//initialize & declare pointers
while (1) {
for (i = 0; i < max; i++) {
value[i] = '\0';
}
if (!(fgets(value,max,f))) {
//segfaults if I try to print out the list inside this block.
break;
}
add(list, value);
//the values are well separated in this statement
printf("id is %s\n", list->head->value);
//This print list works, but works weird as shown above.
print_list(list);
}
fclose(f);
//This print list doesn't work, the list is NULL
print_list(list);
And this is the print list function:
void print_users(llist *list) {
ListNode *e;
if (list->head == NULL) {
printf("NO USERS\r\n");
return;
}
e = list->head;
while (e != NULL) {
puts(e->id);
e = e->next;
}
}
So I don't have a good grasp at all on what you're exactly trying to do here, so we can only do but so much. You may consider posting a MCVE. However, I may be able to give you some pointers on building a linked list. I directly copied your add function into a linked list class that I just hurriedly built, and it worked fine, so there may be something else in your llist class that is causing the issue, or it could be something else in your code. The class and a brief description of the class are listed below.
basic node class
Note: I used templates, but you could just as easily remove the template statements and replace T with any type.
template<typename T>
class node {
private:
T data;
node* next;
public:
// The C++11 rule of 5
// Default Constructor
node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }
// Copy Constructor
node(const node<T>& src) : data(src.data), next(src.next) { }
// Move Constructor
node(node<T>&& src) : data(src.data), next(src.next) {
src.data = T();
src.next = nullptr;
}
// Copy Assignment Operator
node<T>& operator=(const node<T>& src) {
this->data = src.data;
this->next = src.next;
return(*this);
}
// Move Assignment Operator
node<T>& operator=(node<T>&& src) {
this->data = src.data;
this->next = src.next;
src.data = T();
src.next = nullptr;
}
// Destructor
~node() {};
// Some functions to help with encapsulation
void set_next(node* nxt) {
this->next = nxt;
}
void set_data(T value) {
this->data = value;
}
node* get_next() {
return(this->next);
}
T& get_data() {
return(data);
}
};
linked list class body
Since you're using dynamic memory, you need to make sure you adhere to the rule of 3 or 5 depending on whether or not you're using C++11.
template<typename T>
class llist {
private:
node<T>* head;
public:
llist();
llist(const llist& src);
llist(llist&& src);
llist(~llist);
llist& operator=(const llist& src);
llist& operator=(llist&& src);
void push();
void insert();
};
default constructor
Nothing fancy here.
template<typename T>
llist<T>::llist() : head(nullptr) { }
copy constructor
Since you're using dynamic memory this is crucial
template<typename T>
llist<T>::llist(const llist& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
}
move constructor
template<typename T>
llist<T>::llist(llist&& src) {
// delete current nodes
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
// steal the sources list
this->head = src.head;
src.head = nullptr;
}
destructor
template<typename T>
llist<T>::~llist() {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
}
copy assignment operator
template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
return(*this);
}
move assignment operator
template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
this->head = src.head;
src.head = nullptr;
return(*this);
}
push member
this is essentially opposite of your add member.
template<typename T>
void llist<T>push(T data) {
node<T>* new_node = new node<T>(data);
if(this->head) {
node<T>* tmp = this->head;
while(tmp->get_next()) {
tmp = tmp->get_next();
}
tmp->set_next(new_node);
} else {
this->head = new_node;
}
}
insert member
This is essentially your add member.
template<typename T>
void llist<T>insert(T data) {
node<T>* new_node = new node<T>(data, this->head);
this->head = new_node;
}
I don't know if this will help, and you probably already have and know most of this, but I hope it helps.
In this code, it would appear that you attempted to 'malloc' space for a "llist" user defined object.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
First, you tagged this as C++. In C++, you simply must use new and delete. The C++ compiler does not associate "malloc" with the ctor / dtor of your user created object called "llist". And I assure you that you really do want to create these two methods, even when each are simple. Really.
On the other hand, the C++ compiler does provide New and Delete, and will automatically invoke the ctor and dtor when appropriate for both dynamic variables (in heap), and automatic variables (on stack). The compiler will not support this with malloc.
Second, your function init() does not return or otherwise deliver the value of the automatic variable you named "list" to any other scope. (Typically, a list lifetime exceeds the life of any function that uses or creates it.)
So your object "list" only exists within the scope of the function init(), within the lifetime of init(). Not very useful.
So the handle to the list of 'malloc'ed things is lost, no longer accessible to anything else. After init(), where did you plan for the listHead to reside?
Even if you used new (and delete) the code still does not deliver the listHead anywhere.
To further your program, you need perhaps 1 of 2 things:
1) a return (from the function) of the "list" handle (I've been calling it "listHead" as you intended, right?)
llist* init() {
llist *listHead = ...
return(listHead);
}
OR
2) a parameter reference which your init function changes. This places the list head outside of init().
void init( llist** listHead) {
llist *list = ...
*listHead = list;
}
You might look into, and take hints from std::list, which has 40+ methods, though you might only need 10. For the methods you plan to implement, you should strive to conform to and use similar names and parameters.
Perhaps you meant to use a class data attribute with the label list (it is quite difficult to imagine this from what you provided). In this case, you should distinguish data attributes names to help you remember what it is, and that it has a different scope. For instance, I would use m_listHead. The prefix m_ (or often, simply the one char prefix '_') simply indicates to the reader that this symbol is a data attribute of a class. This idea is a common c++ idiom, and not enforced by the compiler, but is often part of a coding-standard.
Good luck.

Copy constructor and dynamic allocation

I would like to ask you how to write a copy constructor (and operator = ) for the following classes.
Class Node stores coordinates x,y of each node and pointer to another node.
class Node
{
private:
double x, y;
Node *n;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {}
void setNode (Node *nn) : n(nn) {}
...
};
Class NodesList (inherited from std:: vector) stores all dynamically allocated Nodes
class NodesList : public std::vector<Node *>
{}
The main program:
int main()
{
Node *n1 = new Node(5,10,NULL);
Node *n2 = new Node(10,10,NULL);
Node *n3 = new Node(20,10,NULL);
n1->setNode(n2);
n2->setNode(n3);
n3->setNode(n2);
NodesList nl1;
nl1.push_back(n1);
nl1.push_back(n2);
nl1.push_back(n3);
//Copy contructor is used, how to write
NodesList nl2(nl1);
//OPerator = is used, how to write?
NodesList nl3 = nl1;
}
I do not want to create a shallow copy of each node but a deep copy of each node. Could I ask you for a sample code with copy constructor?
Each node can be pointed more than once. Let us have such situation, when 3 nodes n[1], n[2], n[3] are stored in the NodesList nl1:
n[1] points to n[2]
n[2] points to n[3]
n[3] points to n[2]
A] Our copy constructor process the node n[1]. It creates a new object n[1]_new represented by the copy of the old object n[1]_old. The node n[2] pointed from n[1]_old still does not exist, so n[2]_new must be also created... The pointer from n1_new to n2_new is set.
B] Then second point n[2] is processed. It can not be created twice, n[2]_new was created in A]. But pointed node n[3] does not exist, so the new object n[3]_new as a copy of an old object n[3]_old is created. The pointer from n2_new to n3_new is set.
C] Node n[3]_new has already been created and n[2]_new. The pointer from n3_new to n2_new is set and no other object will be created...
So the copy constructor should check whether the object has been created in the past or has not...
Some reference counting could be helpful...
There is my solution of the problem. A new data member n_ref storing a new verion of the node n was added:
class Node
{
private:
double x, y;
Node *n, *n_ref;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {n_ref = NULL;}
Node * getNode() {return n;}
Node * getRefNode () {return n_ref;}
void setNode (Node *nn) {this->n = nn;}
void setRefNode (Node *nn) {this->n_ref = nn;}
The copy constructor creates a shallow copy of the node:
Node (const Node *node)
{
x = node->x;
y = node->y;
n = node->n;
n_ref = node->n_ref;
}
The copy constructor for NodesList
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
//Node* n = new Node(**i);
//Node n still has not been added to the list
if ((*i)->getRefNode() == NULL)
{
//Create node
Node *node = new Node(*i);
//Add node to the list
push_back(node);
//Set this note as processed
(*i)->setRefNode(node);
//Pointed node still has not been added to the list
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new pointed node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to node n
node->setNode((*i)->getRefNode());
}
}
//Node n has already been added to the list
else
{
//Get node
Node * node = (*i)->getRefNode();
//Pointed node still has not been added
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to n
node->setNode((*i)->getNode()->getRefNode());
}
}
}
}
Perform a shallow copy in NodeList::NodeList(const NodeList&) and you don't have to worry about cycles breaking the copy operation. Disclaimer: the following is untested, incomplete and may have bugs.
class NodeList {
private:
typedef std::vector<Node*> Delegate;
Delegate nodes;
public:
NodeList(int capacity=16) : nodes() { nodes.reserve(capacity); }
NodeList(const NodeList& from);
virtual ~NodeList();
NodeList& operator=(const NodeList& from);
/* delegated stuff */
typedef Delegate::size_type size_type;
typedef Delegate::reference reference;
typedef Delegate::const_reference const_reference;
typedef Delegate::iterator iterator;
typedef Delegate::const_iterator const_iterator;
size_type size() const { return nodes.size(); }
iterator begin() { return nodes.begin(); }
const_iterator begin() const { return nodes.begin(); }
iterator end() { return nodes.end(); }
const_iterator end() const { return nodes.end(); }
// ...
};
NodeList::NodeList(const NodeList& from)
: nodes(from.size()), flags(NodeList::owner)
{
std::map<Node*, Node*> replacement;
Delegate::const_iterator pfrom;
Delegate::iterator pto;
// shallow copy nodes
for (pfrom=from.begin(), pto=nodes.begin();
pfrom != from.end();
++pfrom, ++pto)
{
replacement[*pfrom] = *pto = new Node(**pfrom);
}
// then fix nodes' nodes
for (pto = nodes.begin(); pto != nodes.end(); ++pto) {
(*pto)->setNode(replacement[(*pto)->getNode()]);
}
}
NodeList::operator=(const NodeList&) can use the copy-swap idiom, the same as Tronic's Node::operator=(const Node&).
This design has a potential memory leak in that a copied NodeList is (initally) the only place that references its nodes. If a temporary NodeList goes out of scope, a poor implementation will leak the Nodes the list contained.
One solution is to proclaim that NodeLists own Nodes. As long as you don't add a Node to more than one NodeList (via NodeList::push_back, NodeList::operator[] &c), NodeList's methods can delete nodes when necessary (e.g. in NodeList::~NodeList, NodeList::pop_back).
NodeList::~NodeList() {
Delegate::iterator pnode;
for (pnode = nodes.begin(); pnode != nodes.end(); ++pnode) {
delete *pnode;
}
}
void NodeList::pop_back() {
delete nodes.back();
nodes.pop_back();
}
Another solution is to use smart pointers, rather than Node*. NodeList should store shared pointers. Node::n should be a weak pointer to prevent ownership cycles.
I would just use std::list<Node> instead of NodesList. Well, let's code...
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
Node* n = new Node(**i);
push_back(n);
}
}
Apparently each Node is only allowed to point to another Node in the same list? Otherwise the "deep copy" of a list needs more definition. Should it not be connected to the original NodeList? Should it not be connected to any original Node? Are copies of Nodes not in the list being copied added to some other list or free-floating?
If all the Node-to-Node pointers are constrained within the NodeList, then perhaps you should store indexes instead of pointers, then no special handling is required.
You should not inherit from standard library containers (because they lack virtual destructors). Instead, include them as member variables in your classes.
Since you want a deep copy, you need these: (rule of three)
Node(Node const& orig): x(orig.x), y(orig.y), n() {
if (orig.n) n = new Node(*orig.n);
}
Node& operator=(Node const& orig) {
// The copy-swap idiom
Node tmp = orig;
swap(tmp); // Implementing this member function left as an exercise
return *this;
}
~Node() { delete n; }
A better idea might be to avoid using pointers entirely and just put your nodes in a suitable container.