Подтвердить что ты не робот

Как ключевое слово friend (Class/Function) разбивает инкапсуляцию на С++?

Некоторые программисты сказали, что "функция друга разрушает инкапсуляцию в С++". и некоторые программисты также сказали: "Функции друга не нарушают инкапсуляцию, а, естественно, расширяют барьер инкапсуляции"

что это значит?..

Если функция friend разрушает инкапсуляцию в С++, то как?

4b9b3361

Ответ 1

Цитата из Часто задаваемые вопросы по С++, которые, я думаю, очень хорошо описывают ситуацию с другом и инкапсулированием.

Нет! Если они используются правильно, они улучшают инкапсуляцию.

Вам часто нужно разделить класс пополам, когда две половины будут иметь разное количество экземпляров или разные времена жизни. В этих случаях, две половинки обычно нуждаются в прямой доступ друг к другу (две половины раньше были в одном классе, поэтому вы не увеличили количество кода который требует прямого доступа к данным состав; вы просто перетасовали код на два класса вместо один). Самый безопасный способ реализации этого состоит в том, чтобы сделать двух половинок друзей друг друга.

Если вы используете знакомых, как описано, вы будете хранить личные вещи частный. Люди, которые не понимают это часто делает наивные попытки избежать используя дружбу в ситуациях, подобных выше, и часто они на самом деле уничтожить инкапсуляцию. Они либо используют публичные данные (гротеск!), или они делают данные, доступные между половинами через public get() и set() функции. Наличие общедоступных get() и set() для частной datum в порядке, только когда частный дата "имеет смысл" извне класса (с точки зрения пользователя). В во многих случаях этот элемент get()/set() функции почти так же плохи, как и данные: они скрывают (только) имя частные данные, но они не скрывают существование частной базы данных.

Ответ 2

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

Я лично считаю, что друзья не нарушают инкапсуляцию, потому что классы не должны быть полностью зависимыми, а скорее взаимозависимыми.

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

Тем не менее, класс Mechanic определенно должен был знать о конкретных внутренних работах автомобиля, но нет смысла строить Механика в Автомобиль.

Здесь приходят друзья. Вы делаете Механика Другом двигателя, тормоза или все, что нужно исправить, и он может это исправить. Он не может исправить это, если все, что он может сделать, это нажать газ/тормоз.

Ответ 3

Инкапсуляция означает, что вы не можете видеть, что внутри, а friend означает, что вы можете видеть внутри. В зависимости от вашей точки зрения friend, следовательно, либо прерывает инкапсуляцию (позволяя другу видеть внутри), либо расширяет его (позволяя разработчику разрешить барьер только для определенных друзей).

FWIW, хорошее эмпирическое правило - сказать, что только вложенные/внутренние классы должны быть объявлены друзьями; это правило можно суммировать как "без дальних друзей", т.е. друзья класса (если они есть) должны быть объявлены в том же заголовочном файле, что и сам класс.

Ответ 4

Я размещаю фрагмент из Stroustrup Дизайн и эволюция С++,

2.10 Модель защиты, стр. 53.
[...]

Декларация о дружбе рассматривалась как механизм, аналогичный механизму защиты, предоставляющей возможность чтения и записи другому. Это является явной и конкретной частью объявление класса. Следовательно, я никогда не видели повторяющиеся утверждения, что a friendдекларация "нарушает инкапсуляцию" как ничего, кроме комбинации невежество и путаница с не-С++ терминология.

Ответ 5

У вас есть две разные школы мысли.

Первый (Java и С#...):: бросает каждую функцию в общедоступной области класса, нужна ли этой функции для доступа к области 'private' или нет.

Второй (С++...):: предоставляет "только" необходимые функции, чтобы этот класс мог выжить и предоставлять функции более высокого уровня в несвязанной коллекции созданного типа.

IMHO:: С++ четко соответствует задачам ООП.

Ответ 6

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

"Инкапсуляция" в реферате также является примитивной системой контроля доступа, за исключением того, что обычно мы говорим, что код, который является "частью класса" , является привилегированным и может манипулировать объектом всеми способами, разрешенными языком. Код, который не является "частью класса" , непривилегирован и должен использовать меньший, возможно, публичный публичный интерфейс.

Итак, для чего "друг"? Если вы так думаете об этом, он предназначен для написания привилегированного кода, который не является функцией-членом. Это необходимо, потому что есть некоторые вещи, которые по техническим причинам не могут быть функциями-членами. Те из них, которые я могу считать безрезультатными, - это перегрузки операторов, где вам нужно преобразование на LHS, а также специализации стандартных функций шаблона алгоритма, таких как std:: swap (последнее из них менее проблематично, поскольку, если есть функция public swap, вряд ли будет вредным для того, чтобы там был общедоступный метод обмена. Но приятно поддерживать тех людей, которые хотят, чтобы интерфейсы были ортогональными).

Вопрос в том, нарушает ли он инкапсуляцию, чтобы иметь привилегированный код, который не является функцией-членом? В Java вы, вероятно, говорите "да", так как Java-код явно "является частью класса", если он в определении класса, а не в классе, если это не так.

В С++ это немного менее понятно, так как определения функций-членов не обязательно должны быть в определении класса для начала. Там очень мало различий между:

// Foo.h
class Foo;
void bar(Foo &);

class Foo {
    friend void bar(Foo &);
public:
    static void baz(Foo &);
};

// Foo.cpp
void bar(Foo &f) {
    // access private members of f
}
void Foo::baz(Foo &f) {
    // access private members of f
}

Я не могу убедить себя, что в любом значащем смысле бар "разрывает инкапсуляцию", тогда как база "сохраняет инкапсуляцию". Даже крошечная степень прагматизма указывает на то, что бар и баз делают то же самое, и, кроме того, хотя бар не является функцией-членом, он явно является "частью класса" так же, как и баз. Единственные различия являются синтаксическими и не имеют ничего общего с инкапсуляцией.

С другой стороны, ясно, что "друг" может быть использован для полного разрыва инкапсуляции, называя все другие классы, о которых вы можете думать как ваш друг (например, сделать все ваши члены общедоступными или защищенными пакетами на Java или сделать всех ваших членов public в С++). "Друг" должен работать с классами, а не только с методами, так что вы можете объявлять вложенные классы в качестве друзей, где это необходимо, и иметь небольшие кластеры тесно связанных классов, которые не вложены (то есть: нарисуйте границы инкапсуляция на более высоком уровне, чем у одного класса). Если вы используете его для плотной связи всех ваших классов, то вы, вероятно, в конечном итоге получите плохой код.

На самом деле это не из-за "друга", поскольку все, что было сделано, дало мне новый синтаксис, чтобы разоблачить участников, которые я мог бы разоблачить как "общедоступные". Но если я подойду к "другу" в качестве обходного пути и использую его, чтобы прорвать свои собственные неадекватные публичные интерфейсы, тогда "друг" фактически дал мне оправдание для плохого дизайна. Я могу поклясться в будущем и посоветую другим поступать аналогичным образом, но по той же причине я клянусь выпить каждый раз, когда я просыпаюсь. Другие могут быть мудрыми или счастливыми, чтобы пользоваться преимуществами без серьезных побочных эффектов.

Таким образом, "друг" сам по себе допускает инкапсуляцию, как это делает арифметика указателя, и так же, как алкоголь позволяет впасть в сточную канаву в 3 часа ночи. Однако с осторожностью и достойным дизайном конкретные использования друга не должны и не должны быть инкапсулирующими. И, если любые потенциальные будущие работодатели читают это, когда я пью, я обычно не перевешиваю; -)

В конечном счете проблема заключается в том, что на каждом языке, который я знаю, интерфейсы ведут себя как наследование, в том смысле, что открытый интерфейс класса включает в себя все члены всех интерфейсов, которые он реализует. Поэтому интерфейс класса больше, чем любой другой. Это не плохо, поскольку отношения "is-a" являются ключевыми для OO. Кроме того, это хорошо соответствует тому, что происходит, когда вы публикуете интерфейс, который может использовать любой клиент. Это, естественно, не учитывает то, что требует много проектов, что для классов по умолчанию имеет небольшой интерфейс, но также предлагает более широкий интерфейс для "суперпользователей". Таким образом, вместо этого большой интерфейс является стандартным, а "подчиненные" относятся к скучным частям.

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

Ответ 7

Ключевое слово friend позволяет другим классам получать доступ к закрытым и защищенным данным в С++. Некоторые считают, что это нарушает инкапсуляцию, другие люди думают, что она расширяет инкапсуляцию до небольшого числа внешних классов, которым необходим доступ к конфиденциальным/защищенным данным. "Прерывание инкапсуляции" означает, что члены и методы обычно являются частными по какой-либо причине, а ключевое слово friend разбивает эту инкапсуляцию.

В коде, который я видел, ключевое слово friend обычно прерывает инкапсуляцию и снижает качество кода, но иногда возникают ситуации, в которых он хорошо используется.

Вот простой учебник по классовой дружбе и наследованию, если вы его раньше не видели:

Дружба и наследование

Ответ 8

Как только вы объявите функцию друга, обратитесь к спецификаторам доступа, и, таким образом, инкапсулированные данные вашего класса станут доступными для этой функции-друга, которая не является частью/методом вашего класса. Таким образом, вы в основном делегируете некоторый контроль над внутренними структурами данных и методами внешней функции, которой вы доверяете

Ответ 9

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

Ответ 10

Если вы нарисуете линию вокруг всех, кто может получить доступ к личным данным, это та часть, в которой данные инкапсулированы.

Если вы используете ключевое слово friend, эту строку нужно рисовать вокруг большего количества вещей. Таким образом, инкапсуляция расширяется или прерывается, в зависимости от вашей точки зрения.

В общем, я препятствую использованию friend. Вместо этого рассмотрите возможность добавления доступа для частных данных.