LinkedList копируют детали реализации конструктора

Я начинаю изучать C++ и поскольку осуществление решает реализовать простое LinkedList класс (Ниже существует часть кода). У меня есть вопрос относительно способа, которым конструктор копии должен быть реализован и лучший способ данные по оригиналу LinkedList должен быть получен доступ.

    template <typename T>
    class LinkedList {

        struct Node {
            T data;
            Node *next;

            Node(T t, Node *n) : data(t), next(n) {};
        };

    public:
        LinkedList();
        LinkedList(const LinkedList&);
        ~LinkedList();

        //member functions
        int size() const;              //done
        bool empty() const;            //done
        void append(const T&);         //done
        void prepend(const T&);        //done
        void insert(const T&, int i); 
        bool contains(const T&) const; //done
        bool removeOne(const T&);      //done
        int  removeAll(const T&);      //done
        void clear();                  //done
        T& last();                     //done
        const T& last() const;         //done
        T& first();                    //done
        const T& first() const;        //done
        void removeFirst();            //done
        T takeFirst();                 //done
        void removeLast();
        T takeLast();


        //delete when finished
        void print();                  
        //end delete

        //operators
        bool operator ==(const LinkedList<T> &other) const;    //done
        bool operator !=(const LinkedList<T> &other) const;    //done
        LinkedList<T>& operator =(const LinkedList<T> &other); //done


    private:
        Node* m_head;
        Node* m_tail;
        int   m_size;

    };

    template<typename T>
    LinkedList<T>::LinkedList() : m_head(0), m_tail(0), m_size(0) {

    }
...

Если мой конструктор копии получает доступ к данным по каждому узлу оригинала LinkedList непосредственно?

template<typename T>
LinkedList<T>::LinkedList(const LinkedList& l) {

    m_head = 0;
    m_tail = 0;
    m_size = 0;

    Node *n = l.m_head;

    // construct list from given list
    while(n) {
        append(n->data);
        n = n->next;
    }
}

Или действительно ли я должен получить доступ к данным через соответствующее средство доступа? (Я знаю, что мне не определили средство (средства) доступа).

Кроме того, я намереваюсь создать пользовательский итератор так, чтобы могло быть возможно выполнить итерации по LinkedList. Я должен использовать в конструкторе копии для доступа к данным по каждому узлу?

Другой вопрос (полностью вне темы, я знаю), когда и/или почему мы должны объявить указатель на a LinkedList

LinkedList<int> *l = new LinkedList<int>(); 

вместо

LinkedList<int> l;
5
задан Bill the Lizard 26 September 2012 в 00:10
поделиться

2 ответа

Я полагаю, что append правильно обработает начальные детали головы / хвоста, да? Если это так, то то, что у вас есть сейчас, великолепно и просто: просмотрите другой список, возьмите его элемент и добавьте копию в мой список. Идеально.

Ну, почти. Используйте список инициализаторов для инициализации переменных-членов:

template<typename T>
LinkedList<T>::LinkedList(const LinkedList& l) :
m_head(0), m_tail(0), m_size(0)
{
 // ...
}

Кроме того, возможно, из-за стиля, это работает вместо цикла while:

// construct list from given list
for (Node *n = l.m_head; n != 0; n = n->next)
    append(m->data);

На самом деле, я бы рекомендовал это вместо этого. Когда у вас есть итераторы, вы бы сделали что-то вроде:

for (const_iterator iter = l.begin(); iter != l.end(); ++iter)
    append(*iter);

Он просто лучше соответствует стилю цикла for. (Инициализировать что-то, что-то проверить, что-то сделать). Хотя для итераторов, вероятно, будет иначе. (Подробнее позже)


Или мне следует получить доступ к данным через соответствующий метод доступа? (Я знаю, что у меня нет определенных средств доступа).

Кроме того, я намерен создать собственный итератор, чтобы можно было выполнять итерацию по LinkedList. Следует ли использовать в конструкторе копирования для доступа к данным на каждом узле?

Эти итераторы являются вашими аксессорами. Вы не хотите раскрывать свои внутренние указатели "голова-хвост", это рецепт катастрофы. Целью класса является , а не раскрытие деталей. Тем не менее, итераторы - это абстрактная оболочка для этих деталей.

Когда у вас есть итераторы, вы можете использовать их для итерации по списку вместо арифметических операций с указателями. Это связано с недавно заданным вопросом . В общем, вы должны использовать свои абстракции для работы с вашими данными. Итак, если у вас есть итераторы , вы должны использовать их для итерации по данным.

Большинство классов, которые предоставляют итераторы, также предоставляют способ вставки данных с учетом начального и конечного итераторов. Обычно это называется insert , например: insert (iterBegin, iterEnd) . Это проходит через итераторы, добавляя данные в список.

Если бы у вас была такая функциональность, ваш конструктор копирования был бы просто:

insert(l.begin(), l.end()); // insert the other list's entire range

Где insert реализован аналогично циклу for, который у нас был выше.


Другой вопрос (совершенно не по теме, я знаю), когда и / или почему мы должны объявлять указатель на LinkedList

LinkedList * l = new LinkedList (); вместо LinkedList l;

Первое - это динамическое размещение, второе - автоматическое (стековое) распределение. Вы должны предпочесть распределение стека. Это почти всегда быстрее и безопаснее (поскольку вам не нужно ничего удалять). Фактически, концепция под названием RAII полагается на автоматическое хранение, поэтому деструкторы гарантированно запускаются.

Используйте динамическое размещение только при необходимости.

3
ответ дан 15 December 2019 в 06:24
поделиться

Я думаю, что реализация вашего собственного связанного списка по-прежнему является очень ценным упражнением, поскольку оно помогает вам изучить детали указателей, структур данных и т. Д. Просто убедитесь, что вы не используете свой класс связанного списка в реальном коде, поскольку есть многие существующие библиотеки, которые уже написаны и протестированы. Лучший код - это код, который вам не нужно писать. См. std :: list .

Я не вижу проблем в том, как вы реализовали конструктор копирования. Возможно, вам стоит переместить код в специальную функцию копирования и вызвать ее из конструктора, чтобы уменьшить количество кода, который вам нужно поддерживать. Но в целом использование деталей реализации вашего класса из самого класса не только приемлемо, но и во многих случаях предпочтительнее, так как это будет как можно быстрее.

Что касается создания списка с новым и его создания в стеке, это решение, которое применимо не только к вашему классу, но и к структурам данных в целом. Чрезмерно упрощенное практическое правило: выделяйте в стеке, если можете, выделяйте в куче, если нужно. Вам может понадобиться, например, если вам нужно, чтобы список пережил функцию, в которой он создан.

И снова возвращаясь к тому, чтобы не изменять свой собственный код вручную, если вы все же решите использовать new для выделения в куче, вам следует использовать интеллектуальные указатели вместо того, чтобы пытаться самостоятельно управлять памятью. Приобретите эту привычку прямо сейчас. Не ждите, пока вы начнете работать над «настоящим» кодом. Многие люди, с которыми вы столкнетесь, будут бесполезны в ваших поисках лучшего кода и будут настаивать на том, чтобы «просто использовать new ».

0
ответ дан 15 December 2019 в 06:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: