C++, реальные примеры, когда friend классы и функции были бы уместнее, чем иное решение?

Я задумался над целесообразностью использования в C++ дружественных функций и классов. Пытался найти внятные примеры, когда это действительно необходимо и дает плюсов больше, чем минусов. Таких примеров мне удалось найти совсем немного.

Может быть, у кого-нибудь есть примеры из жизни, когда friend классы и функции были очень удобны?


Ответы (3 шт):

Автор решения: user7860670

В С++ модификаторы доступа одинаково работают для всех пользователей класса. Использование friend по сути просто делает все кишки класса доступными для дружественной сущности, даже если ей нужно только несколько, и поэтому обычно считается сомнительной практикой. Возможность дать доступ к одной части членов класса для одних пользователей, к другой - для других, а к третьей - третьим (как это делается в языке Еiffel например) из коробки отсутствует. Однако такой высокогранулированный доступ можно обеспечить используя идиому Attorney-Client:

class Client 
{
    friend class AttorneyAB;
    private: void A(int a);
    private: void B(float b);
    private: void C(double c);
};

class AttorneyAB
{
    friend class User;
    private: static void callA(Client & c, int a) { c.A(a); } 
    private: static void callB(Client & c, float b) { c.B(b); }
};

class User
{
// User now has access to only Client::A and Client::B through the Attorney.
};
→ Ссылка
Автор решения: AR Hovsepyan

Стандартная библиотека часто для реализации той или иной сущности, использует дружественные классы. Например:

  1. класс локализации locale определяет класс facet, деструктор которого защищенный, чтобы все фасеты, наследованные от locale::facet и объявляющие класс locale своим другом, могли быть уничтожены только данной локализацией.
  2. Класс std::valarray является другом классов, std::slice_array, std::gslice_array, std::mask_array, std::indirect_array, чьи конструкторы являются закрытыми, дабы их экземпляры могли создаваться только обьектами std::valarray. (Эти классы являются вспомогательными для std::valarray, например для разного представления возвращаемого типа operator[]).

Для того, чтобы существующая функция(класс) могла пользоваться закрытыми членами(в том числе, создать или уничтожать обьект, когда конструктор и деструктор не имеют публичный спецификатор доступа), нужно, чтобы она была другом класса...

→ Ссылка
Автор решения: Andrey Sv

Начну немного издалека. Вы пишете шаблонный класс имеющий несколько параметров:

template <typename T1, typename T2, int sz> class MyCoolClass {
};

Теперь для этого класса нужно перегрузить операторы +,-,*,/. Только, как то не хочется отдельно перегружать для каждого типа, давайте сделаем перегрузку шаблонной. Я опущу замещения для разных типов:

template <typename T1, typename T2, int sz> class MyCoolClass {
  public:
    template <class Ti> MyCoolClass operator + (const Ti v) const
    {
         // ....
    }
    // Остальные -,*,/ 
    // ...
};

Все пока хорошо. Выражения типа MyCoolClass a = MyCoolClass() + 5 работают замечательно. Но, вот выражения MyCoolClass a = 5 + MyCoolClass() не работают. Ладно, значит надо перегрузить операторы глобально. Вот только теперь написать шаблонную перегрузку будет не очень удобно, даже пример приводить неохота. И вот в этом случае дружественная шаблонная функция просто спасение:

template <typename T1, typename T2, int sz> class MyCoolClass {
  public:
    template <class Ti> MyCoolClass operator + (const Ti v) const
    {
         // ....
    }
    // Остальные -,*,/ 
    // ...
    template <class Ti> friend MyCoolClass operator + (const Ti v, MyCoolClass& v2) 
    {
         // ....
    }
};

Обратите внимание, параметры шаблонного класса указывать просто не нужно, что значительно сокращает и упрощает подобные функции. Поэтому в данном случае дружественные функции однозначно полезны. И хотя это только один пример, мне кажется он достаточно веский для существования такого механизма.

→ Ссылка