Почему дружба по крайней мере необязательно наследуется в C ++? Я понимаю, что транзитивность и рефлексивность запрещены по очевидным причинам (я говорю это только для того, чтобы избежать простых ответов на часто задаваемые вопросы), но отсутствие чего-то в этом роде virtual friend class Foo;
меня озадачивает. Кто-нибудь знает историческую подоплеку этого решения? Была ли дружба всего лишь ограниченным приемом, который с тех пор нашел свое применение в нескольких малоизвестных респектабельных целях?
Отредактируйте для пояснения: я говорю о следующем сценарии, а не о том, что дочерние элементы A подвергаются воздействию B или одновременно B и его дочерних элементов. Я также могу представить, как необязательно предоставить доступ к переопределению функций друзей и т. Д.
class A {
int x;
friend class B;
};
class B {
// OK as per friend declaration above.
void foo(A& a, int n) { a.x = n; }
};
class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };
Принятый ответ: как утверждает Локи , эффект можно более или менее смоделировать, создав защищенные прокси-функции в дружественных базовых классах, поэтому нет строгой необходимости предоставлять дружбу классу или иерархии виртуальных методов. Мне не нравится необходимость в шаблонных прокси (которыми фактически становится база друзей), но я полагаю, что это было сочтено предпочтительнее языкового механизма, который, скорее всего, будет неправильно использоваться большую часть времени. Я думаю, что, вероятно, пора мне купить и прочитать книгу Stroupstrup « Дизайн и эволюция C ++» , которую, как я видел, рекомендует здесь достаточно людей, чтобы лучше понять эти типы вопросов ...
Я думаю, что ответ на ваш первый вопрос заключается в следующем: «Имеют ли друзья вашего отца доступ к вашим интимным комнатам?»
источник
Класс с друзьями может раскрыть своего друга через функции доступа, а затем предоставить доступ через них.
class stingy { int pennies; friend class hot_girl; }; class hot_girl { public: stingy *bf; int &get_cash( stingy &x = *bf ) { return x.pennies; } }; class moocher { public: // moocher can access stingy's pennies despite not being a friend int &get_cash( hot_girl &x ) { return x.get_cash(); } };
Это обеспечивает более тонкий контроль, чем необязательная транзитивность. Например,
get_cash
может бытьprotected
или может применяться протокол доступа с ограниченным временем выполнения.источник
;)
Стандарт C ++, раздел 11.4 / 8
Если дружба будет унаследована, то класс, который не должен быть другом, внезапно получит доступ к внутренним компонентам вашего класса, а это нарушит инкапсуляцию.
источник
Потому что это просто ненужно.
Использование
friend
ключевого слова само по себе подозрительно. С точки зрения сцепления это худшие отношения (намного опережающие наследование и композицию).Любые изменения во внутреннем устройстве класса могут повлиять на друзей этого класса ... вы действительно хотите неизвестное количество друзей? Вы даже не смогли бы перечислить их, если бы те, кто унаследовал от них, также могли быть друзьями, и вы бы каждый раз подвергались риску взлома кода своих клиентов, что, конечно же, нежелательно.
Я открыто признаю, что для домашних заданий / домашних проектов зависимость часто не рассматривается. В небольших проектах это не имеет значения. Но как только несколько человек работают над одним и тем же проектом, и это разрастается до десятков тысяч строк, вам нужно ограничить влияние изменений.
Это приносит очень простое правило:
Изменение внутренних компонентов класса должно влиять только на сам класс.
Конечно, вы, вероятно, повлияете на его друзей, но здесь два случая:
std::ostream& operator<<(...)
, что здесь, которая не является членом чисто случайно из языковых правилЯ бы порекомендовал использовать простой метод:
class Example; class ExampleKey { friend class Example; ExampleKey(); }; class Restricted { public: void forExampleOnly(int,int,ExampleKey const&); };
Этот простой
Key
шаблон позволяет вам объявить друга (в некотором смысле), фактически не предоставляя ему доступ к вашим внутренним компонентам, тем самым изолируя его от изменений. Кроме того, он позволяет этому другу одалживать свой ключ опекунам (например, детям) при необходимости.источник
Предположение: если класс объявляет в качестве друга какой-то другой класс / функцию, это потому, что этому второму объекту нужен привилегированный доступ к первому. Какая польза от предоставления второму объекту привилегированного доступа к произвольному количеству классов, производных от первого?
источник
B
что у вас будет доступ ко всем классам, унаследованным отA
...Производный класс может наследовать только то, что является «членом» базы. Объявление друга не является членом класса дружбы.
и далее
Поскольку «друг» изначально не является членом базового класса, как он может быть унаследован производным классом?
источник
Дружественная функция в классе присваивает функции свойство extern. т.е. extern означает, что функция была объявлена и определена где-то вне класса.
Следовательно, это означает, что функция друга не является членом класса. Таким образом, наследование позволяет вам наследовать только свойства класса, а не внешние вещи. А также, если для дружественных функций разрешено наследование, то наследуется сторонний класс.
источник
Friend хорош в наследовании, как интерфейс стиля для контейнера. Но для меня, как первый сказал, C ++ не имеет распространяемого наследования
class Thing; //an interface for Thing container's struct IThing { friend Thing; protected: int IThing_getData() = 0; }; //container for thing's struct MyContainer : public IThing { protected: //here is reserved access to Thing int IThing_getData() override {...} }; struct Thing { void setYourContainer(IThing* aContainerOfThings) { //access to unique function in protected area aContainerOfThings->IThing_getData(); //authorized access } }; struct ChildThing : public Thing { void doTest() { //here the lack of granularity, you cannot access to the container. //to use the container, you must implement all //function in the Thing class aContainerOfThings->IThing_getData(); //forbidden access } };
Для меня проблема C ++ заключается в отсутствии очень хорошей детализации для управления всем доступом из любого места для чего угодно:
Friend Thing может стать другом Thing. *, чтобы предоставить доступ ко всем дочерним элементам Thing.
И еще, друг [именованная область] Thing. *, Чтобы предоставить доступ для точного, находятся в классе Container через специальную именованную область для друга.
Хорошо, хватит мечты. Но теперь вы знаете интересное использование слова friend.
В другом порядке вы также можете найти интересным, что все классы дружны с собой. Другими словами, экземпляр класса может
без ограничений вызывать всех членов другого экземпляра с таким же именем:
class Object { private: void test() {} protected: void callAnotherTest(Object* anotherObject) { //private, but yes you can call test() from //another object instance anotherObject)->test(); } };
источник
Простая логика: «У меня есть подруга Джейн. То, что мы подружились вчера, не означает, что все ее друзья мои ».
Мне все еще нужно одобрить эту индивидуальную дружбу, и уровень доверия будет соответствующим.
источник