Я читал часто задаваемые вопросы по C ++ и мне было интересно узнать об этом friend
объявлении. Лично я никогда не использовал это, однако я заинтересован в изучении языка.
Что является хорошим примером использования friend
?
Немного дольше читая FAQ, мне нравится идея <<
>>
перегрузки и добавления операторов в друзья этих классов. Однако я не уверен, как это не нарушает инкапсуляцию. Когда эти исключения могут оставаться в рамках строгости ООП?
c++
oop
encapsulation
friend
Питер Мортенсен
источник
источник
Ответы:
Во-первых (ИМО) не слушайте людей, которые говорят, что
friend
это бесполезно. Это полезно. Во многих ситуациях у вас будут объекты с данными или функциями, которые не предназначены для публичного доступа. Это особенно верно для больших кодовых баз со многими авторами, которые могут только поверхностно знакомы с различными областями.Существуют альтернативы спецификатору друга, но часто они громоздки (конкретные классы уровня cpp / маскированные определения типов) или не защищены от ошибок (комментарии или соглашения об именах функций).
На ответ;
Спецификатор
friend
разрешает назначенному классу доступ к защищенным данным или функциям в классе, делающем оператор Friend. Например, в приведенном ниже коде любой может попросить ребенка назвать свое имя, но только мать и ребенок могут изменить имя.Вы можете продолжить этот простой пример, рассмотрев более сложный класс, такой как Window. Вполне вероятно, что в Window будет много элементов функций / данных, которые не должны быть общедоступными, но необходимы для связанного класса, такого как WindowManager.
источник
friend
улучшает инкапсуляцию.friend
предоставляет избирательный доступ к членам, как иprotected
делает. Любой детальный контроль лучше, чем предоставление публичного доступа. Другие языки также определяют механизмы избирательного доступа, рассмотрим C #internal
. Наиболее негативная критика вокруг использованияfriend
связана с более тесной связью, которая обычно считается плохой вещью. Однако в некоторых случаях более тесная связь - именно то, что вам нужно, иfriend
дает вам эту силу.friend
является, а не на мотивирующем примере. Пример Window / WindowManager лучше, чем показанный пример, но слишком расплывчатый. Этот ответ также не касается инкапсуляции части вопроса.На работе мы широко используем друзей для тестирования кода . Это означает, что мы можем обеспечить надлежащую инкапсуляцию и сокрытие информации для основного кода приложения. Но также у нас может быть отдельный тестовый код, который использует друзей для проверки внутреннего состояния и данных для тестирования.
Достаточно сказать, что я бы не использовал ключевое слово friend как важный компонент вашего дизайна.
источник
friend
Ключевое слово имеет несколько хороших целей. Вот два вида использования, сразу же видимых для меня:Определение друга
Определение «друг» позволяет определить функцию в области действия класса, но эта функция будет определяться не как функция-член, а как свободная функция окружающего пространства имен, и обычно не будет видна, за исключением поиска, зависящего от аргумента. Это делает его особенно полезным для перегрузки операторов:
Частный базовый класс CRTP
Иногда вы обнаруживаете, что политике нужен доступ к производному классу:
Вы найдете необдуманный пример для этого в этом ответе. Другой код, использующий это в этом ответе. База CRTP преобразует этот указатель, чтобы иметь возможность доступа к полям данных производного класса с помощью указателей на данные-члена.
источник
P<C>
вtemplate<template<typename> class P> class C : P<C> {};
заявлении «Использование шаблона класса C требует аргументов шаблона». Вы сталкивались с такими же проблемами или, возможно, знаете решение?FlexibleClass
внутриFlexibleClass
должно косвенно относиться к своему собственному типу.@roo : Инкапсуляция здесь не нарушена, потому что сам класс диктует, кто может получить доступ к своим закрытым членам. Инкапсуляция будет нарушена только в том случае, если это может быть вызвано извне класса, например, если вы
operator <<
объявите «Я друг классаfoo
».friend
заменяет использованиеpublic
, а не использованиеprivate
!На самом деле, C ++ FAQ отвечает уже на это .
источник
friend
не является исключением. Единственное реальное наблюдение здесь - это то, что C ++ обеспечивает инкапсуляцию только во время компиляции. И вам не нужно больше слов, чтобы сказать это. Остальное чушь. Итак, в заключение: этот раздел FQA не стоит упоминать.Канонический пример - перегрузка оператора <<. Другим распространенным применением является предоставление доступа к вашим внутренним компонентам помощникам или администраторам.
Вот несколько рекомендаций, которые я слышал о друзьях C ++. Последний особенно запоминающийся.
источник
friend
я думаю.Как это нарушит инкапсуляцию?
Вы нарушаете инкапсуляцию, когда разрешаете неограниченный доступ к элементу данных. Рассмотрим следующие классы:
c1
это , очевидно , не инкапсулируется. Любой может прочитать и изменитьx
его. У нас нет способа обеспечить какой-либо контроль доступа.c2
очевидно инкапсулирован. Нет публичного доступа кx
. Все, что вы можете сделать, это вызватьfoo
функцию, которая выполняет значимую операцию над классом .c3
? Это менее инкапсулировано? Это разрешает неограниченный доступ кx
? Разрешает ли доступ к неизвестным функциям?Нет. Это позволяет точно одной функции получить доступ к закрытым членам класса. Так же, как и
c2
сделал. И точно так жеc2
, единственная функция, которая имеет доступ, это не «какая-то случайная, неизвестная функция», а «функция, указанная в определении класса». Также какc2
мы можем увидеть, просто посмотрев определения классов, полный список тех, у кого есть доступ.Так как именно это менее инкапсулировано? Такое же количество кода имеет доступ к закрытым членам класса. И каждый, у кого есть доступ, указан в определении класса.
friend
не нарушает инкапсуляцию. Это заставляет некоторых программистов на Java чувствовать себя неловко, потому что когда они говорят «ООП», они на самом деле означают «Java». Когда они говорят «Инкапсуляция», они не означают «частные члены должны быть защищены от произвольного доступа», а «класс Java, где единственными функциями, которые могут получить доступ к закрытым членам, являются члены класса», хотя это полная чушь для несколько причин .Во-первых, как уже показано, это слишком ограничивает. Нет причин, по которым методы друзей не должны делать то же самое.
Во- вторых, это не является ограничительным достаточно . Рассмотрим четвертый класс:
Это, согласно вышеупомянутому менталитету Java, прекрасно инкапсулировано. И все же, это позволяет абсолютно любому читать и изменять x . Как это вообще имеет смысл? (подсказка: это не так)
Итог: инкапсуляция - это возможность контролировать, какие функции могут получить доступ к закрытым членам. Речь идет не о том, где именно находятся определения этих функций.
источник
Еще одна распространенная версия примера Эндрю, страшный код-куплет
Вместо того, чтобы беспокоиться о том, что обе строки всегда выполняются вместе и в согласованном порядке, вы можете сделать методы приватными и иметь функцию-друга для обеспечения согласованности:
Другими словами, вы можете уменьшить общедоступные интерфейсы и применять инварианты, которые пересекают классы и объекты в функциях друзей.
источник
addChild
функции-члену также установить родителя?setParent
, так как вы не хотите, чтобы клиенты меняли родителя, так как вы будете управлять им вaddChild
/removeChild
категории функций.Вы контролируете права доступа для участников и функций, используя частное / защищенное / публичное право? поэтому, если предположить, что идея каждого из этих трех уровней ясна, тогда должно быть ясно, что мы что-то упустили ...
Объявление члена / функции как защищенного, например, довольно общее. Вы говорите, что эта функция недоступна для всех (за исключением наследственного ребенка, конечно). Но как насчет исключений? каждая система безопасности позволяет вам иметь некоторый тип «белого списка», верно?
Таким образом, друг позволяет вам иметь гибкую изоляцию твердого объекта, но позволяет создать «лазейку» для вещей, которые вы считаете оправданными.
Я думаю, что люди говорят, что это не нужно, потому что всегда есть дизайн, который будет обходиться без него. Я думаю, что это похоже на обсуждение глобальных переменных: вы никогда не должны их использовать, всегда есть способ обойтись без них ... но в действительности вы видите случаи, когда это оказывается (почти) самым элегантным способом. .. Я думаю, что это то же самое с друзьями.
хорошо, что это не совсем способ смотреть на это. Идея состоит в том, чтобы контролировать ВОЗ, которая может получить доступ к тому, что наличие или отсутствие функции настройки имеет мало общего с ней.
источник
friend
лазейка? Это позволяет методам, перечисленным в классе, получить доступ к своим закрытым членам. Это все еще не позволяет произвольному коду получить к ним доступ. Как таковая она ничем не отличается от публичной функции-члена.Я нашел удобное место, чтобы использовать доступ друзей: Unittest личных функций.
источник
Friend пригодится, когда вы создаете контейнер и хотите реализовать итератор для этого класса.
источник
У нас была интересная проблема, возникшая в компании, в которой я раньше работал, где мы использовали друга, чтобы оказать достойное влияние. Я работал в нашем фреймворковом отделе, мы создали базовую систему уровня движка над нашей собственной ОС. Внутри у нас была структура классов:
Все эти классы были частью структуры и поддерживаются нашей командой. Игры, производимые компанией, были построены на основе этого фреймворка, созданного одним из детей Games. Проблема заключалась в том, что у Game были интерфейсы к различным вещам, к которым SinglePlayer и TwoPlayer требовался доступ, но мы не хотели, чтобы они были доступны вне рамок классов. Решением было сделать эти интерфейсы частными и разрешить TwoPlayer и SinglePlayer доступ к ним через дружбу.
По правде говоря, весь этот вопрос мог бы быть решен лучшей реализацией нашей системы, но мы были заперты в том, что имели.
источник
Короткий ответ будет: использовать друга когда это действительно улучшается инкапсуляцию. Улучшение читаемости и удобства использования (операторы << и >> являются каноническим примером) также является хорошей причиной.
Что касается примеров улучшения инкапсуляции, то хорошими кандидатами являются классы, специально предназначенные для работы с внутренними компонентами других классов (тестовые классы приходят на ум).
источник
<<
и,>>
как правило, друзья, а не участники, потому что делать из них членов будет неудобно. Конечно, я говорю о случае, когда этим операторам необходим доступ к частным данным; иначе дружба бесполезна.operator<<
иoperator>>
члены класса значения вместо не членов или членовi|ostream
не будут обеспечивать желаемый синтаксис, и я не предлагаю его. « Я говорю о случае, когда этим операторам нужен доступ к частным данным ». Я не совсем понимаю, почему операторам ввода / вывода нужен доступ к закрытым членам.Создатель C ++ говорит, что не нарушает принцип инкапсуляции, и я процитирую его:
Более чем понятно ...
источник
Другое использование: friend (+ виртуальное наследование) может быть использовано, чтобы избежать наследования от класса (иначе: «сделать класс неразборчивым») => 1 , 2
Из 2 :
источник
Чтобы сделать TDD много раз, я использовал ключевое слово «друг» в C ++.
Может ли друг знать обо мне все?
Обновлено: я нашел этот ценный ответ о ключевом слове "друг" с сайта Бьярна Страуструпа .
источник
Вы должны быть очень осторожны с тем, когда и где вы используете
friend
ключевое слово, и, как и вы, я использовал его очень редко. Ниже приведены некоторые примечания по использованиюfriend
и альтернативам.Допустим, вы хотите сравнить два объекта, чтобы увидеть, равны ли они. Вы можете либо:
Проблема с первой опцией заключается в том, что это может быть МНОГО аксессоров, которые (немного) медленнее, чем прямой доступ к переменным, труднее читать и громоздки. Проблема второго подхода заключается в том, что вы полностью нарушаете инкапсуляцию.
Было бы неплохо, если бы мы могли определить внешнюю функцию, которая могла бы получить доступ к закрытым членам класса. Мы можем сделать это с
friend
ключевым словом:Метод в
equal(Beer, Beer)
настоящее время имеет прямой доступ кa
иb
«s частных пользователей (которые могут бытьchar *brand
,float percentAlcohol
и т.д. Это довольно надуманный пример, вы бы скорее обратитьсяfriend
к перегруженным== operator
, но мы вернемся к этому.Несколько вещей, на которые стоит обратить внимание:
friend
НЕ является функцией-членом классаpublic
!)Я действительно использую только
friends
тогда, когда это гораздо сложнее сделать иначе. В качестве другого примера, функции многого вектора математика часто создаются вfriends
связи с совместимостьюMat2x2
,Mat3x3
,Mat4x4
,Vec2
,Vec3
,Vec4
и т.д. И это просто так гораздо проще быть друзьями, а не должен использовать аксессор везде. Как уже указывалось,friend
часто полезно применительно к<<
(действительно удобно для отладки)>>
и, возможно, к==
оператору, но также может использоваться для чего-то вроде этого:Как я уже сказал, я не
friend
очень часто использую, но время от времени это именно то, что вам нужно. Надеюсь это поможет!источник
Что касается оператора << и оператора >>, то нет веских причин дружить с этими операторами. Это правда, что они не должны быть функциями-членами, но они также не должны быть друзьями.
Лучше всего создать общедоступные функции печати (ostream &) и чтения (istream &). Затем напишите оператор << и оператор >> в терминах этих функций. Это дает дополнительное преимущество, позволяя вам сделать эти функции виртуальными, что обеспечивает виртуальную сериализацию.
источник
Я использую только ключевое слово friend для проверки защищенных функций. Некоторые скажут, что вы не должны тестировать защищенную функциональность. Однако я нахожу этот очень полезный инструмент при добавлении новых функций.
Тем не менее, я не использую ключевое слово непосредственно в объявлениях класса, вместо этого я использую изящный шаблон-хак для достижения этой цели:
Это позволяет мне сделать следующее:
Работает на GCC и MSVC по крайней мере.
источник
В C ++ ключевое слово «друг» полезно при перегрузке операторов и создании моста.
Теперь первый вариант не годится, потому что если нам нужно снова перегрузить этот оператор для какого-то другого класса, мы должны снова внести изменения в класс «ostream».1.) Ключевое слово Friend при перегрузке оператора:
Пример перегрузки оператора: Допустим, у нас есть класс «Point», который имеет две переменные с плавающей запятой
«x» (для координаты x) и «y» (для координаты y). Теперь мы должны перегрузить
"<<"
(оператор извлечения), что если мы вызываем"cout << pointobj"
его, он выведет координаты x и y (где pointobj - это объект класса Point). Для этого у нас есть два варианта:Вот почему второй вариант самый лучший. Теперь компилятор может вызывать
"operator <<()"
функцию:Поскольку мы реализовали перегрузку в классе Point. Таким образом, чтобы вызвать эту функцию без объекта, мы должны добавить
"friend"
ключевое слово, потому что мы можем вызвать функцию друга без объекта. Теперь объявление функции будет следующим:"friend ostream &operator<<(ostream &cout, Point &pointobj);"
2.) Ключевое слово Friend при создании моста:
предположим, нам нужно создать функцию, в которой мы должны получить доступ к закрытому члену двух или более классов (обычно называемых «мостом»). Как это сделать:
чтобы получить доступ к закрытому члену класса, он должен быть членом этого класса. Теперь для доступа к закрытому члену другого класса каждый класс должен объявить эту функцию как функцию друга. Например: предположим, есть два класса A и B. Функция
"funcBridge()"
хочет получить доступ к закрытому члену обоих классов. Тогда оба класса должны объявить"funcBridge()"
как:friend return_type funcBridge(A &a_obj, B & b_obj);
Я думаю, что это поможет понять ключевое слово друга.
источник
В качестве ссылки для объявления друзей говорится:
Так что, как напоминание, в некоторых ответах есть технические ошибки, которые говорят, что
friend
могут посещать только защищенные участники.источник
Пример дерева - довольно хороший пример: реализация объекта в нескольких разных классах без наследования.
Возможно, вам также может понадобиться защита конструктора и принуждение людей использовать вашу «фабрику друзей».
... Ладно, честно говоря, вы можете жить без этого.
источник
Нет, это только односторонняя дружба: `(
источник
Один конкретный пример, который я использую,
friend
это при создании классов Singleton .friend
Ключевое слово позволяет мне создать функцию доступа, которая является более кратким , чем всегда иметь «GetInstance ()» метод на классе.источник
friend
это не нужно особое «оправдание», при добавлении функции члена нет.Дружественные функции и классы обеспечивают прямой доступ к закрытым и защищенным членам класса, чтобы избежать нарушения инкапсуляции в общем случае. Большая часть использования с ostream: мы хотели бы иметь возможность печатать:
Однако для этого может потребоваться доступ к закрытым данным Point, поэтому мы определяем перегруженный оператор
Однако есть очевидные последствия инкапсуляции. Во-первых, теперь класс или функция друга имеет полный доступ ко ВСЕМ членам класса, даже к тем, которые не относятся к его потребностям. Во-вторых, реализации класса и друга теперь запутаны до такой степени, что внутреннее изменение в классе может сломать друга.
Если вы рассматриваете друга как расширение класса, то, по логике говоря, это не проблема. Но, в таком случае, зачем было сначала выделять друга?
Чтобы достичь того же, чего преследуют «друзья», но не нарушая инкапсуляцию, можно сделать следующее:
Инкапсуляция не нарушена, класс B не имеет доступа к внутренней реализации в A, но результат такой же, как если бы мы объявили B другом A. Компилятор оптимизирует вызовы функций, поэтому это приведет к тому же результату. инструкции как прямой доступ.
Я думаю, что с помощью «друг» просто ярлык с спорной выгодой, но определенной платой.
источник
Это не может быть реальной ситуацией использования, но может помочь проиллюстрировать использование друга между классами.
Клуб Хаус
Класс членов
Удобства
Если вы посмотрите на отношения этих классов здесь; ClubHouse предлагает различные виды членства и членства. Все члены являются производными от супер или базового класса, поскольку все они имеют общий идентификатор и перечислимый тип, и внешние классы могут получать доступ к своим идентификаторам и типам через функции доступа, которые находятся в базовом классе.
Однако благодаря такой иерархии членов и их производных классов и их отношений с классом ClubHouse единственным из производных классов, обладающих «особыми привилегиями», является класс VIPMember. Базовый класс и другие 2 производных класса не могут получить доступ к методу joinVIPEvent () ClubHouse, однако у класса VIP-члена есть такая привилегия, как если бы он имел полный доступ к этому событию.
Таким образом, с VIPMember и ClubHouse это улица с двусторонним движением, где другие классы участников ограничены.
источник
При реализации древовидных алгоритмов для класса, код фреймворка, который дал нам проф, имел класс дерева как друга класса узлов.
На самом деле это не приносит никакой пользы, кроме того, что позволяет вам получить доступ к переменной-члену без использования функции настройки.
источник
Вы можете использовать дружбу, когда разные классы (не наследуя один от другого) используют частных или защищенных членов другого класса.
с http://www.cplusplus.com/doc/tutorial/inheritance/ .
Вы можете увидеть этот пример, где метод, не являющийся членом, получает доступ к закрытым членам класса. Этот метод должен быть объявлен в этом классе как друг класса.
источник
Возможно, я что-то упустил из ответов выше, но другой важной концепцией инкапсуляции является скрытие реализации. Сокращение доступа к частным элементам данных (подробности реализации класса) позволяет значительно упростить модификацию кода позже. Если друг напрямую обращается к частным данным, любые изменения в полях данных реализации (личные данные) нарушают код доступа к этим данным. Использование методов доступа в основном устраняет это. Довольно важно, я думаю.
источник
Вы могли бы придерживаться самых строгих и чистых принципов ООП и гарантировать, что ни у одного члена данных для какого-либо класса даже нет средств доступа, поэтому все объекты должны быть единственными, которые могут знать о своих данных, и единственный способ воздействовать на них - через косвенные сообщения , т.е. методы.
Но даже в C # есть ключевое слово внутренней видимости, а у Java есть доступность на уровне пакета по умолчанию для некоторых вещей. На самом деле C ++ приближается к идеалу ООП, минимизируя компромисс видимости в классе, точно указав , какой другой класс и только другие классы могут его видеть.
Я на самом деле не использую C ++, но если бы у C # были друзья, я бы это использовал вместо внутреннего модификатора сборки-глобального , который я действительно часто использую. На самом деле это не нарушает инкапсуляцию, потому что единица развертывания в .NET - это сборка.
Но есть и атрибут InternalsVisibleTo (otherAssembly), который действует как механизм друзей перекрестной сборки . Microsoft использует это для сборок визуального конструктора .
источник
Друзья также полезны для обратных вызовов. Вы можете реализовать обратные вызовы как статические методы
где
callback
вызовыlocalCallback
внутри, и в немclientData
есть ваш экземпляр. По моему мнению,или...
Это позволяет другу быть определенным чисто в cpp как функция в стиле c, а не загромождать класс.
Точно так же шаблон, который я видел очень часто, состоит в том, чтобы поместить все действительно закрытые члены класса в другой класс, который объявлен в заголовке, определен в cpp и добавлен в список друзей. Это позволяет кодеру скрывать большую часть сложности и внутренней работы класса от пользователя заголовка.
В шапке:
В CPP,
Становится легче скрывать вещи, которые нижестоящему не нужно видеть таким образом.
источник