Simple imperative doubly linked list I've started learning C++ and still making my first program. This is a continuation of my last post.

My original aim was to make a linked list at a professional level. But have now changed it to a doubly linked list, to simplify the code. It implements a couple of public functions that should increase the usability of the list significantly.

I also tried to implement an internal iterator. But didn't see a benefit to making a bidirectional iterator, for internal use, to replace getNode.

Whilst I think I improved all problems mentioned in my last post. I mostly focused on implementing the sentinel. I don't think it's a sentinel, as it's doing more than just being my end condition. And so I'd love some feedback on my implementation. But any and all critiques are wanted.

#include &lt;iostream&gt;

template &lt;class T&gt;
class LinkedList {
private:
    class Node {
    public:
        Node* next;
        Node* prev;
        T data;
        // For the sentinal
        Node(): data('\0'), prev(this), next(this) {}
        Node(T data, Node* before)
                : data(data) {
            prev = before;
            next = before-&gt;next;
            prev-&gt;next = this;
            next-&gt;prev = this;
        }
        ~Node() {
            prev-&gt;next = next;
            next-&gt;prev = prev;
        }
        Node(Node const&amp;)            = delete;
        Node&amp; operator=(Node const&amp;) = delete;
    };
    Node sentinal;
    Node* getNode(int index) {
        if (index &gt;=  0) {
            int i = 0;
            for (Node* node = sentinal.next; node != &amp;sentinal; node = node-&gt;next) {
                if (i == index) {
                    return node;
                }
                i++;
            }
        } else {
            int i = -1;
            for (Node* node = sentinal.prev; node != &amp;sentinal; node = node-&gt;prev) {
                if (i == index) {
                    return node;
                }
                i--;
            }
        }
        throw std::out_of_range("List doesn't contain that item.");
    }
public:
    LinkedList() {}
    ~LinkedList() {
        clear();
    }
    LinkedList(LinkedList const&amp;)            = delete;
    LinkedList&amp; operator=(LinkedList const&amp;) = delete;

    void clear() {
        while (sentinal.next != &amp;sentinal) {
            delete sentinal.next;
        }
    }

    int length() {
        int length = 0;
        for (Node* node = sentinal.next; node != &amp;sentinal; node = node-&gt;next) {
            length += 1;
        }
        return length;
    }

    void append(T data) {
        new Node(data, sentinal.prev);
    }

    T get(int index) {
        return getNode(index)-&gt;data;
    }

    void insert(T data, int index) {
        new Node(data, getNode(index)-&gt;prev);
    }

    T pop(int index=-1) {
        Node* node = getNode(index);
        T data = node-&gt;data;
        delete node;
        return data;
    }
};

int main() {
    LinkedList&lt;char&gt; list;
    list.append('h');
    list.append('l');
    list.append('l');
    list.append('l');
    list.append('o');
    list.insert('e', 1);
    std::cout &lt;&lt; list.pop(2) &lt;&lt; list.get(-1) &lt;&lt; '\n';
    for (int i=0; i &lt; list.length(); i++) {
        std::cout &lt;&lt; list.get(i);
    }
    std::cout &lt;&lt; '\n';
    list.clear();
    std::cout &lt;&lt; list.length() &lt;&lt; '\n';
}
getNode does not replace an iterator. It always starts at the beginning of the list. It means that scanning over the list has a quadratic time complexity.
A Node default constructor is dubious. It assumes that T can be constructed from a NUL character. It seems like an unnecessary restriction. Assuming that T itself has a default constructor, and using it is more logical.
length() method is expected to have a constant time complexity. A linear one is at best surprising. I recommend to make a member, to be incremented at each insertion, and decremented on each removal.