В настоящее время я просматриваю учебник http://www.cplusplus.com, и здесь я наткнулся на этот раздел: http://www.cplusplus.com/doc/tutorial/inheritance.html, который касается темы функции друзей и классы друзей в C++.
У меня вопрос: когда при создании программы целесообразно использовать дружбу?
Единственная подсказка, которую я получил, - это пример внутри статьи, демонстрирующий функцию друга, которая «дублирует» объект.





Для этого есть несколько действительно хороших практических правил в C++ FAQ Lite Маршалла Клайна.
В целом все хорошо, но посмотрите, в частности, на "Друзья нарушают инкапсуляцию?", чтобы увидеть примеры правильного их использования и когда лучше всего разделить классы и объявить их друзьями.
Дружественные функции существуют для того, чтобы представлять бесплатные функции как непрерывную часть интерфейса класса. Есть несколько мест, где бесплатные функции являются частью интерфейса класса. Пример: Предположим, у вас есть класс произвольной точности BigNum. Вот несколько очевидных кандидатов на функции друзей:
// binary operators where BigNum isn't the left-hand operand
BigNum operator+ (int, BigNum);
BigNum operator- (int, BigNum);
// stream operators
std::ostream &operator<< (std::ostream &os, const BigNum &num);
std::istream &operator>> (std::istream &is, BigNum &num);
Теперь, учитывая эти два примера, во многих случаях бинарные операторы не обязательно должны быть друзьями (например, я могу реализовать int + BigNum, делегировав BigNum + int, который является функцией-членом и, следовательно, уже имеет полный доступ). Но все зависит от ваших потребностей в производительности и от того, что вы готовы раскрыть с помощью общедоступных функций-членов класса.
Я согласен с операторами << и >>. Но для вашего другого примера - не будет ли проще, если вы просто определите преобразование из int в BigNum?
Да, в случае BigNum, потому что int всегда потенциально является BigNum. В других случаях может не иметь смысла создавать объект из LHS оператора.
Автоматические преобразования обычно - плохая идея. Они могут странным образом взаимодействовать с определением типа и перегрузкой функций.
Одним из хороших приложений для дружеских классов является шаблон проектирования Memento.
Поскольку друзья обычно нарушают механизмы сокрытия данных, их следует использовать только тогда, когда это действительно необходимо. Если ваш дизайн в значительной степени зависит от друзей, вероятно, это в чем-то неверно.
Пример того, когда вы не можете обойтись без друзей, - переопределение операторов потока << и >>. Также классы друзей часто используются при реализации некоторых паттернов проектирования - на ум приходит «Итератор».
Что ж, переопределение операторов потока не означает их дружбу. Если некоторая информация об объекте отображается с использованием оператора <<, мы можем предположить, что этот бит информации является общедоступным и, следовательно, существует геттер для доступа к нему.
@Luc: Причина переопределения операторов потока как друзей заключается в том, что класс потока (ostream или istream) находится слева от оператора. Поэтому, если вы хотите переопределить оператор с помощью функции-члена, он должен быть членом ostream или istream. Но вы не можете этого сделать, поэтому вы используете друга
Друзья не нарушают сокрытие данных. Друзья просто позволяют инкапсуляции класса включать в себя бесплатные функции, а не только функции-члены.
@Tom: «Просто» разрешив это, вы должны изменить что-то вне класса, когда вы измените внутреннее представление класса. И это нарушение сокрытия данных.
@Boyan - Нет. Друг является частью класса, даже если он синтаксически не является его членом. Друга, вероятно, следует реализовать в том же исходном файле, что и остальной класс, anyhoo.
@Tom: Я думаю, мы говорим об одном и том же, используя разные слова. Для меня то, что находится за пределами класса, не следует называть «частью класса», но, вероятно, ваша точка зрения также верна. Но я думаю, вы согласитесь с тем, что друзей, частично или не расставаясь, нельзя чрезмерно использовать.
@Boyan - да, похоже, мы согласны по поводу концепций. И да, друзей следует использовать только в случае крайней необходимости (т.е. эквивалентная функциональность недоступна или возможна через общедоступный интерфейс).
Я использовал только дружественные методы и свойства для использования внутри самого класса для клонирования самого себя или присвоения себе другого объекта. Это было в значительной степени заменено, мы реорганизовали дизайн для реализации паттерна Memento Design в нашем дизайне.
Memento создается с помощью тех же механизмов, которые используются для сохранения и загрузки объекта. то есть Memento создает поток, объект записывает в него, и memento может быть передан любому другому объекту того же класса, чтобы сделать его точной копией объекта, создающего memento.
Иногда бывают случаи, когда объекты должны работать очень близко друг к другу, и в этом случае я упрощаю их взаимодействие, объединяя их за другим объектом. Переменные, которые были бы «дружественными» переменными и видимыми только для других объектов, являются частными для класса, выполняющего агрегирование.
Я использовал функции друзей как часть интерфейса класса, который должен вызываться из кода C. Мы объявляем дружественные функции в отдельном заголовке с искажением в стиле C и компилируем их исходный код как C++.