Когда разумно использовать дружбу в ООП?

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

У меня вопрос: когда при создании программы целесообразно использовать дружбу?

Единственная подсказка, которую я получил, - это пример внутри статьи, демонстрирующий функцию друга, которая «дублирует» объект.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
601
5

Ответы 5

Для этого есть несколько действительно хороших практических правил в C++ FAQ Lite Маршалла Клайна.

В целом все хорошо, но посмотрите, в частности, на "Друзья нарушают инкапсуляцию?", чтобы увидеть примеры правильного их использования и когда лучше всего разделить классы и объявить их друзьями.

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

John 12.12.2012 21:52

Дружественные функции существуют для того, чтобы представлять бесплатные функции как непрерывную часть интерфейса класса. Есть несколько мест, где бесплатные функции являются частью интерфейса класса. Пример: Предположим, у вас есть класс произвольной точности 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?

Boyan 15.12.2008 12:41

Да, в случае BigNum, потому что int всегда потенциально является BigNum. В других случаях может не иметь смысла создавать объект из LHS оператора.

Tom 15.12.2008 17:41

Автоматические преобразования обычно - плохая идея. Они могут странным образом взаимодействовать с определением типа и перегрузкой функций.

David Thornley 15.12.2008 23:43

Одним из хороших приложений для дружеских классов является шаблон проектирования Memento.

Поскольку друзья обычно нарушают механизмы сокрытия данных, их следует использовать только тогда, когда это действительно необходимо. Если ваш дизайн в значительной степени зависит от друзей, вероятно, это в чем-то неверно.

Пример того, когда вы не можете обойтись без друзей, - переопределение операторов потока << и >>. Также классы друзей часто используются при реализации некоторых паттернов проектирования - на ум приходит «Итератор».

Что ж, переопределение операторов потока не означает их дружбу. Если некоторая информация об объекте отображается с использованием оператора <<, мы можем предположить, что этот бит информации является общедоступным и, следовательно, существует геттер для доступа к нему.

Luc Touraille 15.12.2008 12:02

@Luc: Причина переопределения операторов потока как друзей заключается в том, что класс потока (ostream или istream) находится слева от оператора. Поэтому, если вы хотите переопределить оператор с помощью функции-члена, он должен быть членом ostream или istream. Но вы не можете этого сделать, поэтому вы используете друга

Boyan 15.12.2008 12:40

Друзья не нарушают сокрытие данных. Друзья просто позволяют инкапсуляции класса включать в себя бесплатные функции, а не только функции-члены.

Tom 16.12.2008 05:32

@Tom: «Просто» разрешив это, вы должны изменить что-то вне класса, когда вы измените внутреннее представление класса. И это нарушение сокрытия данных.

Boyan 16.12.2008 09:34

@Boyan - Нет. Друг является частью класса, даже если он синтаксически не является его членом. Друга, вероятно, следует реализовать в том же исходном файле, что и остальной класс, anyhoo.

Tom 16.12.2008 17:37

@Tom: Я думаю, мы говорим об одном и том же, используя разные слова. Для меня то, что находится за пределами класса, не следует называть «частью класса», но, вероятно, ваша точка зрения также верна. Но я думаю, вы согласитесь с тем, что друзей, частично или не расставаясь, нельзя чрезмерно использовать.

Boyan 16.12.2008 21:53

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

Tom 17.12.2008 00:27

Я использовал только дружественные методы и свойства для использования внутри самого класса для клонирования самого себя или присвоения себе другого объекта. Это было в значительной степени заменено, мы реорганизовали дизайн для реализации паттерна Memento Design в нашем дизайне.

Memento создается с помощью тех же механизмов, которые используются для сохранения и загрузки объекта. то есть Memento создает поток, объект записывает в него, и memento может быть передан любому другому объекту того же класса, чтобы сделать его точной копией объекта, создающего memento.

Иногда бывают случаи, когда объекты должны работать очень близко друг к другу, и в этом случае я упрощаю их взаимодействие, объединяя их за другим объектом. Переменные, которые были бы «дружественными» переменными и видимыми только для других объектов, являются частными для класса, выполняющего агрегирование.

Другие вопросы по теме