C++ Moscow: post #104 — TG.ME

Интересный пример, когда объявление friend T (где T — шаблонный параметр) имеет смысл:

template <typename T>
class Badge {
friend T;
Badge() { }
};


Допустим, у нас есть какой-нибудь публичный метод, и предполагается, что круг его пользователей ограничен лишь одним классом, например:

class VFS {
...
public:
void register_device(Device&);
void unregister_device(Device&);
};


Предполагается, что эти методы будут вызываться внутри конструкторов (и деструкторов соответственно) Device и нигде (и никем) больше. Но как закрепить наше ожидание на уровне API? Сейчас эти методы может вызывать каждый.

Распространенное решение — вынести (un)register_device в приватную часть, а Device объявить дружественным:

class VFS {
...
private:
friend class Device;
void register_device(Device&);
void unregister_device(Device&);
};


И это действительно запрещает всем кроме Device обращаться к данным методам. Но это так же разрешает Device обращаться ко всем остальным приватным членам VFS! Кажется, будто мы позволяем ему слишком многое.

Выразить же желаемое наиболее четко и ясно нам помогает как раз таки класс Badge, приведенный в самом начале статьи: мы можем оставить методы (un)register_device в публичной части, но при этом сделать их первым аргументом значение Badge<Device>:

class VFS {
...
public:
void register_device(Badge<Device>, Device&);
void unregister_device(Badge<Device>, Device&);
};


Теперь чужак не может использовать эти методы, потому что он не может сконструировать Badge<Device> (его конструктор приватный и доступен только Device), а сам Device может вызывать их где и как пожелает:

Device::Device()
{
VFS::instance().register_device({}, *this);
}
🔥16👍3
July 14, 2024 1.7K 3 10