Doubly linked list reimplemented with smart pointers I need some suggestions on my reimplementation of a doubly linked list with smart pointers since I'm new to smart pointers in C++.

I read some docs on smart pointers and found out there are shared_ptr&lt;Obj&gt;, unique_ptr&lt;Obj&gt; and weak_ptr&lt;Obj&gt;. I only use shared_ptr&lt;Obj&gt; in my current code, but I'm not sure how to use unique_ptr&lt;Obj&gt; in my doubly linked list. I would like someone can give me some pointers on how and where to use unique_ptr&lt;Obj&gt; in my DLL.

class Node{
    public:
        int data;
        Node(int data){
            this-&gt;data = data;
        }
    public:
        shared_ptr&lt;Node&gt; prev;
        shared_ptr&lt;Node&gt; next;
};

class LinkedList{
    public:
        shared_ptr&lt;Node&gt; head;
        shared_ptr&lt;Node&gt; tail;
    public:
        LinkedList(){}

        void fun(){
            printf("fun()\n");
        }

        void append(shared_ptr&lt;Node&gt; node){
            if(!head){
                head = node;
                tail = head;
            }else{
                tail-&gt;next = node;
                node-&gt;prev = tail;
                tail = node;
            }
        }

        void print(){
            shared_ptr&lt;Node&gt; curr = head;
            while(curr){
                printf("[%d]\n", curr-&gt;data);
                curr = curr-&gt;next;
            }
        }
        void remove(shared_ptr&lt;Node&gt; node){
            shared_ptr&lt;Node&gt; curr = head;
            if(node &amp;&amp; curr){
                while(curr){
                    if(curr == node){
                        if(curr-&gt;prev == nullptr){
                            if(curr-&gt;next == nullptr){
                                // [curr] 
                                node.reset();
                                head.reset();
                                tail.reset();
                            }else{
                                // [curr]-&gt;[] 
                                shared_ptr&lt;Node&gt; next = curr-&gt;next;
                                curr-&gt;next.reset();
                                next-&gt;prev.reset();
                                head = next;
                            }
                        }else{
                            if(curr-&gt;next == nullptr){
                                // []-&gt;[curr] 
                                shared_ptr&lt;Node&gt; prev = curr-&gt;prev;
                                curr-&gt;prev.reset();
                                prev-&gt;next.reset();
                                tail = prev;
                            }else{
                                // []-&gt;[curr]-&gt;[]
                                shared_ptr&lt;Node&gt; next = curr-&gt;next;
                                shared_ptr&lt;Node&gt; prev = curr-&gt;prev;
                                next-&gt;prev = prev;
                                prev-&gt;next = next;
                            }
                        }
                    }
                    curr = curr-&gt;next;
                }
            }
        }
};
Summary

This is obviously a personal opinion, but I don't really like the interface you've defined for your list.  By defining your methods in terms of a shared_ptr&lt;Node&gt;, you're making the caller responsible for constructing Nodes.  It seems like Node should be an implementation detail of your list.  Otherwise, since the caller has complete access to the Node they can iterate the list and modify it without the LinkedList class being called at all.

I'd define the interface in terms of the Data that is to be stored, currently this is an int, so you would have something like this:

void append(int newValue)


Or you may want to define your list in terms of a template and take in a pointer of the relevant type.  This could be a shared_ptr if ownership of the data doesn't pass solely to your list, or it could be a unique_ptr if the node that references it will be the owner of the Data.

Append

Looking at your current implementation, what happens if a Node is passed in that already has its prev and next pointers populated (with the given interface I could assume that I could insert a Node chain).  These pointers aren't checked, they're overwritten.  This is part of why I don't like the interface...

Remove

What happens if you're asked to remove a node that isn't in the list?  Currently nothing.  It feels like this method should either be returning an indicator that the object has been removed, or more likely throwing an exception to indicate that it hasn't..