Мне нужен навязчивый, отсортированный двойной список. Я не хочу использовать boost :: intrusive, поэтому я делаю это сам и сталкиваюсь с проблемой
Для двусвязного списка есть несколько операций, вот лишь одна из них, которая поможет проиллюстрировать мою точку зрения:
template<typename LIST, typename NODE>
void insertAfter(LIST *list, NODE *node, NODE *newNode)
{
newNode->prev_ = node;
newNode->next_ = node->next_;
if(nullptr == node->next_)
list->last_ = newNode;
else
node->next_->prev_ = newNode;
node->next_ = newNode;
}
Теперь предположим, что у меня есть набор объектов, находящихся в одном из таких списков, НО я хочу, чтобы их кишки были приватными:
struct Object
{
private:
Object *prev_, *next_;
};
Теперь я создаю свой список (пожалуйста, не обращайте внимания на тот факт, что, когда список пуст, будет исключение nullptr …).
struct List
{
Object *first_, *last_;
void addObject(Object *o)
{
insertAfter(this, last_, o); // not correct when list empty
}
};
Это не скомпилируется, так как prev_ и next_ являются частными, а insertAfter не имеет доступа. Это может быть легко решено с этим:
// Fwd decl
struct List;
struct Object
{
friend void insertAfter<List, Object>(List *, Object *, Object *);
private:
Object *prev_, *next_;
};
struct List
{
Object *first_, *last_;
void addObject(Object *o)
{
insertAfter(this, last_, o);
}
};
Но это открывает дыру в доступе, так что любой может использовать insertAfter для воздействия на приватные члены Object. Что я действительно хочу, так это чтобы List был другом Object.
Я мог бы решить эту проблему, не используя шаблоны для своей операции со связанным списком (вместо этого используйте простые макросы), но это, очевидно, имеет свои недостатки. Какой правильный путь сюда?
Как насчет чего-то подобного?
template<class ObjectType>
class List
{
ObjectType *first_, *last_;
public:
void addObject(ObjectType *o)
{
insertAfter(this, last_, o);
}
void insertAfter(ObjectType *node, ObjectType *newNode)
{
newNode->prev_ = node;
newNode->next_ = node->next_;
if(nullptr == node->next_)
this->last_ = newNode;
else
node->next_->prev_ = newNode;
node->next_ = newNode;
}
};
class Object
{
private:
Object *prev_, *next_;
friend class List<Object>;
};
int main() {}
Я не могу понять, как это действительно менее оскорбительно, чем то, что вы уже делали: код шаблона в любом случае встроен, поэтому вы не можете помешать людям переписывать ваш класс по своему вкусу. Просто расслабьтесь и имейте достаточную степень доверия к своим клиентам 🙂
Вы можете инкапсулировать навязчивые данные, что-то вроде:
template <typename Object>
class List
{
public:
class PrivateData
{
friend class List;
public: // minimal stuff which should be public go here
PrivateData() : prev_(nullptr), next_(nullptr) {}
private:
// other stuff
Object *prev_, *next_;
};
// Other stuff
};
И тогда в вашем объекте:
class Object
{
public:
List<Object>::privateData listData_;
private:
// internal data.
};
Как и все вещи в ListData
являются частными, пользователь не может ничего сделать с listData_
но могут быть доступны List
который друг.