More specifically, I am wondering why we use pointers in a typical linked list implementation. Are there any problems that the following implementation of a Node might cause?
template <typename T>
class Node {
T data;
Node<T>& next;
Node<T>& prev;
};
Is there some reason we should use pointers here instead of references?
You can't set references after creating them, which makes a non-mutable linked-list implementation somewhat tricky. (You'd need to wrap the references in objects that you can re-crate when you want to change the reference).
There's also no way to set a NULL value on a reference, so representing the ends of your list would require some imagination.
Probably better to stick to pointers in a linked list, or even better, use std::list<>.
Lvalue references can't replace pointers; they do different things.
An lvalue reference must be initialized with an lvalue and the lvalue reference will refer to that object for the rest of it's lifetime. It cannot be rebound. This presents two immediate problems for your list node.
How do you start the list? You want to construct a node that has no "previous" yet the prev member must be initialized with a Node object. You could conceivably use a Node whose prev is it self to represent the head of a list, but this is working around the poor choice of lvalue reference. (E.g. Node<T> emptylist = { T(), emptylist, emptylist }; //eurgh)
Second, how do you manipulate the list? You can't change the bindings of next and prev meaning that the only way to alter the list would be to construct a completely new set of nodes and copy every single data element.
Related
Can we implement a link list without using the head pointer means by using a simple variable of the head instead of the pointer of the head ?
Yes. If you are implementing a circular linked list with a sentinel node, the sentinel node can be the simple variable that also serves as the head.
Alternatively, you could use a std::optional instance to serve as the head.
In specific cases you could, but in general not. And why would you want to? Here are some reasons, I could think of now. Take for example this code:
template<class T>
class Node
{
private:
T value;
Node<T> *next;
};
class MyLinkedList
{
private:
bool isEmpty; // indicates wether the list is empty or not
Node head; // Head as member
};
But there are several major flaws with this code:
You would always need to care about isEmpty when adding or deleting, or doing anything with the list
You can't initialize head if T has no default constructor
When deleting the last element you have to call the destructor of object that technically remains in scope.
When deleting the last element and then deleting the empty list the destructor of Node::value will be called twice
Don't know if those are all reasons, but I think, just #2 is a big enough problem to not consider this.
Of course you could use std::optional, but that's just a pointer with a wrapper. which even works, without a default constructor, so could be an alternative. Alltough it would be used in the same way as a (smart) pointer, so it's not "a simple variable of the head".
This is for a homework assignment in a cpp course and I'm looking for some best practises.
I've got a rather complex class that I would like to store in a linkedlist class. I must implement my own, including a node class. I need to only store this single relatively complex type I'm working on.
Should I make the node's data type a reference to the complex type, or should I make it a pointer to it instead?
Option 1:
class Node
{
public:
Node* prev;
Node* next;
ComplexType *data;
}
Option 2:
class Node
{
public:
Node* prev;
Node* next;
ComplexType &data;
}
Assuming I will also need to handle the different cases of copying value when working with the reference solution. Which one is considered more correct? And why?
I would go for option 2. Your prev and next need to be pointers, since they can change. A nodes data shouldn't change in a list. Only its position in the list should change by altering the prev/next pointers. In my opinion its always the best if you can prevent using pointers where possible, but in this case your prev/next member should be pointers, since they can change.
As soon as a reference is initialised, it cannot be changed again to reference anything else. Therefore your list will be more useful/flexible if you stick with pointers.
Presumably you need the freedom to have null data, as well as changing it after construction. In this case, you would need pointers of some variety.
I'm a little confused about implementing a doubly linked list where the data in the list are pointers.
The private part of my linked list class looks like:
private:
struct node {
node* next;
node* prev;
T* o;
};
node* first; // The pointer to the first node (NULL if none)
node* last; // The pointer to the last node (NULL if none)
unsigned int size_;
As you can see, the list is full of pointers to objects rather than just plain old objects, which makes it a little more confusing to me.
The following is the description in the spec:
Note that while this list is templated across the contained type, T, it inserts and removes only pointers to T, not instances of T. This ensures that the Dlist implementation knows that it owns inserted objects, it is responsible for copying them if the list is copied, and it must destroy them if the list is destroyed.
Here is my current implementation of insertFront(T* o):
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o);
insert->next = first;
insert->prev = last;
first = insert;
}
This seems wrong though. What if T doesn't have a copy constructor? And how does this ensure sole ownership of the object in the list?
Could I just do:
insert->o = o;
It seems like this is not safe, because if you had:
Object* item = new Object();
dlist.insertFront(item);
delete item;
Then the item would be also be destroyed for the list. Is this correct? Is my understanding off anywhere?
Thanks for reading.
Note: While this looks like homework, it is not. I am actually a java dev just brushing up my pointer skills by doing an old school project.
When you have a container of pointers, you have one of the two following usage scenarios:
A pointer is given to the container and the container takes responsibility for deleting the pointer when the containing structure is deleted.
A pointer is given to the container but owned by the caller. The caller takes responsibility for deleting the pointer when it is no longer needed.
Number 1 above is quite straight-forward.
In the case of number 2, it is expected that the owner of the container (presumably also the caller) will remove the item from the container prior to deleting the item.
I have purposely left out a third option, which is actually the option you took in your first code example. That is to allocate a new item and copy it. The reason I left it out is because the caller can do that.
The other reason for leaving it out is that you may want a container that can take non-pointer types. Requiring it to be a pointer by always using T* instead of T may not be as flexible as you want. There are times when you should force it to be a pointer, but I can't think of any use (off the top of my head) for doing this for a container.
If you allow the user to declare Dlist<MyClass*> instead of Dlist<MyClass> then the owner of that list is implicitly aware that it is using pointers and this forces them to assume scenario Number 2 from above.
Anyway, here are your examples with some commentary:
1. Do not allocate a new T item unless you have a very good reason. That reason may simply be encapsulation. Although I mentioned above that you shouldn't do this, there are times when you may want to. If there is no copy constructor, then your class is probably plain-old-data. If copying is non-trivial, you should follow the Rule of Three.
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o); //<-- Follow rule of three
insert->next = first;
insert->prev = last;
first = insert;
}
2. This is what you would normally do
insert->o = o;
3. You must not delete your item after inserting. Either pass ownership to your container, or delete the item when neither you nor the container requires it anymore.
Object* item = new Object();
dlist.insertFront(item);
delete item; //<-- The item in the list is now invalid
I have a class:
class node
{
public:
node& parent;
}
I want to set the parent value when I know its right value:
node parent;
...
node n; // here node.parent is a not valid value
n.parent = parent;
But I have to set it's value in the constructor too. How can I do?
You can't change what variable a reference references. So if you can't initialize it in the constructor, you don't want a reference. You can use a regular pointer, but it's probably better to use some kind of smart pointer appropriate to your particular use. The correct answer depends primarily on how the lifetime of the referenced object is managed.
If you want to use references and not pointers because you want to suggest that the class instance does not own the parent node, then you can use std::reference_wrapper from C++11's <functional>.
I would advise against using smart pointers (except maybe std::weak_ptr) if the parent holds references to the children and the children hold references to the parent. Using smart pointers in this case would create a cyclic dependency, which means your objects would never get destroyed.
I have a class Stack, using template, one of its methods is "push", which is written below:
template <class T>
void Stack<T>::push(T _data){
Node<T>* temp = new Node<T>;
temp->data = _data;
temp->next = head;
head = temp;
}
The stack works well with int, double, string, char....
But it says
prog.cpp:32: note: synthesized method ‘Node<Tree>::Node()’ first required here
when I use a class "Tree" as data type.
I don't understand, why it works with "string" but not with "Tree", they are both classes, not primitive types.
http://ideone.com/NMxeF
(Ignore the other error, my IDE only gives one error at line 32 and some warnings)
Help!
Edit after reading the actual code (the "note" shown above is fairly misleading about the real problem).
Looking at the code, where you try to use new Node<T>;, that needs a default constructor for T (which in this case is Tree) because your Node template contains an instance of T:
struct Node {
T data; // <--- instance of T, not being initialized in your code.
Node *next;
};
Tree doesn't have a default constructor, so that fails (and the note is showing you where the default constructor would be needed).
You have a few choices about how to fix that. The most obvious would be for a Node to hold either a pointer or a reference to a T instead of containing an actual instance of T.
Another would be to have Node's constructor take a reference to a (probably const) T, and copy that T into the Node:
class Node {
T data;
Node *next;
public:
Node(T const &dat) : data(dat), next(0) {}
};
The choice between these two approaches is fairly fundamental. If you have Node store a pointer/reference to T, then it will be the responsibility of calling code to ensure the passed object remains valid as long as the Node exists. The node and calling code will share access to a single instance of T.
By contrast, if you copy the passed object into the Node, then this copy will be destroyed when the Node is destroyed. The original T (Tree, in your case) you passed to the Node will remain the responsibility of the calling code, and the Node will take responsibility for its copy.
In the usual case, you'd tend to favor the latter -- it gives cleaner semantics, and keeps ownership of the data clear. In the case of a Tree, however, you probably don't want to copy an entire tree into a Node if you can avoid it. One compromise position would be to use something like a Node<shared_ptr<Tree> > instead. The shared_ptr can keep copying fast and cheap, while avoiding writing a Node that's only suitable for a few kinds of objects and situations. That also makes fairly explicit that you're storing only a pointer that gives shared access to the original object.
Do you have a default constructor for Tree? If not, that might be your problem: Node holds in its data member a Tree type that must be default constructed when you call new Node<Tree>.
To fix, you can modify Node's constructor to take data and next as a parameter, so you don't require default constructor on its template type (you still need assignment operator to be available).