Начало класса:
template<class T>
class List : public Collection<T> {
private:
class Node {
private:
Node* _next;
Node* _prev;
public:
T _data;
};
Первая ошибка доступа:
~List() {
while (_head) {
_tail = _head->_next; //я не вижу _next!!!
delete _head;
_head = _tail;
}
}
Не хотелось бы оставлять next и prev открытыми из очевидных предположений. Можно что-то изменить?
Есть разные варианты, например, сделать класс другом или добавить в него открытые функции доступа к полям.
В своем стремлении все закрыть вы сделали "вещь в себе" - класс, в котором есть только закрытые члены, к которым может обращаться только сам класс. Но если к ним может обращаться только класс, то логично попросить его об этом? Т.е. добавить функции-члены для работы с закрытыми членами-данными, нет?
Иначе зачем вам этот чемодан, закрытый на ключ, который находится в чемодане? :)
Согласно стандарту C++ (14.7 Nested classes):
1 A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 14) shall be obeyed.
То есть члены внешнего класса не имеют специальных прав доступа к членам вложенного класса.
Вы могли бы класс List
объявить дружественным к вложенному классу Node
.
Так как у вас класс Node
вложенный и определен как private-член внешнего класса, то нет никакой необходимости объявлять еще его члены как private.
Вы не описываете этого в своем вопросе, но проблемы с нежелательным доступом к членам такого класса вполне возможны во вполне естественных ситуациях. Например, если ваш класс List
предоставляет методы для работы с подсписками или узлами связного списка, т.е. методы вроде splice
, detach
и т.п. Например в таком коде
class List
{
class Node
{
public:
Node *next;
...
};
public:
Node *detach_first();
};
пользователь такого класса List
будет иметь возможность сделать
int main()
{
List list;
...
auto *p = list.deatch_first();
// `p->next` - свободно доступен здесь
}
и после этого получить свободный доступ к публичным полям класса List::Node
. Более того, пользователь может сделать
using ListNode = std::remove_pointer_t<decltype(list.deatch_first())>;
и вот уже у вас в руках есть сам закрытый тип list::Node
, без применения каких-либо "хаков".
Помещение класса List::Node
в приватный раздел класса List
лишает внешних пользователей лишь возможности впрямую упоминать имя List::Node
во внешнем коде, но не более того. Вышеприведенный код, например, использует auto
и обходится без упоминания имени List::Node
, получая при этом совершенно законный доступ ко всему интерфейсу List::Node
. То есть никакой ситуации "закрытого чемодана в закрытом чемодане" тут нет. Вам лишь запрещено называть внутренний чемодан "чемоданом". (Все это, кстати, реализуемо и в С++98, без auto
и decltype
, хоть там это и требует дополнительных телодвижений.)
Тот факт, что защита, предоставляемая таким закрытым объявлением вложенного класса, настолько "тонка" и узконаправленна, зачастую является сюрпризом даже для подготовленных С++ программистов.
Однако, возвращаясь к исходной задаче (в моем ее понимании):
Если ваш класс List
, согласно вашему замыслу, действительно должен давать внешним пользователям доступ к значениям типа List::Node
, то имеет смысл все таки сделать List::Node
открытым (public) членом List
- скрывать его никакого смысла нет.
Если вы хотите при этом запретить доступ к реализации List::Node
, то следует сделать все члены List::Node
закрытыми (private), а класс List
сделать другом класса List::Node
.
Но лучше все таки пойти по другому пути: оставить класс List::Node
закрытым в List
и сделать все члены List::Node
открытыми. Последнее позволит List
доступаться к членам List::Node
. Но при этом надо позаботиться о том, чтобы внешний пользователь никогда не получал прямого доступа к значениям типа List::Node
. Все внешние методы, вроде detach_fist
, splice
и т.д. должны работать не через List::Node *
, а через публичный List::NodeHandle
- некий opaque тип, который прячет внутри себя значение List::Node *
, но не открывает этого факта внешнему пользователю. То есть в данном случае мы предпринимаем дополнительные усилия для того, чтобы "закрытый чемодан" действительно был закрытым.
Например, простейшим вариантом такого List::NodeHandle
будет просто void *
. Внутри методов List
вы просто будете приводить его к List::Node *
. В тривиальных контейнерах в качестве List::NodeHandle
может выступать их итератор. В более сложных может понадобится отдельная абстракция. Обратите внимание, что начиная с С++17 стандартная библиотека С++ поддерживает подобный интерфейс для классов стандартных ассоциативных контейнеров, основанный именно на "node handle" подходе: https://en.cppreference.com/w/cpp/container/node_handle
Виртуальный выделенный сервер (VDS) становится отличным выбором
Задан целочисленный массив N*M (N - количество строк, M - столбцов)Каждая строка массива упорядочена по возрастанию
На каникулы задали лабораторную работу (сделать программу, которая будет хранить метаданные файла, и текст содержащийся в нем, и различные...