Я предполагаю, что 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 = новый LinkedList (); вместо LinkedList l;
Первое - динамическое распределение, второе - автоматическое (стековое) распределение. Вы должны предпочесть распределение стека. Это почти всегда быстрее и безопаснее (поскольку вам не нужно ничего удалять). Фактически, концепция RAII основана на автоматическом хранении, поэтому деструкторы гарантированно будут работать.
Используйте динамическое распределение только тогда, когда это необходимо.