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 ofTmay 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 ofDlist<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
Titem 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.2. This is what you would normally do
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.