C ++ Как избежать использования шаблонов друзей с помощью самодельных навязчивых списков

Мне нужен навязчивый, отсортированный двойной список. Я не хочу использовать 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.
Я мог бы решить эту проблему, не используя шаблоны для своей операции со связанным списком (вместо этого используйте простые макросы), но это, очевидно, имеет свои недостатки. Какой правильный путь сюда?

0

Решение

Как насчет чего-то подобного?

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() {}

Я не могу понять, как это действительно менее оскорбительно, чем то, что вы уже делали: код шаблона в любом случае встроен, поэтому вы не можете помешать людям переписывать ваш класс по своему вкусу. Просто расслабьтесь и имейте достаточную степень доверия к своим клиентам 🙂

1

Другие решения

Вы можете инкапсулировать навязчивые данные, что-то вроде:

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 который друг.

0

По вопросам рекламы [email protected]