дружественная функция может быть дружественна более чем одному классу
Дружественные функции и классы в C++
В этом руководстве мы научимся создавать дружественные функции и классы на C++ с помощью примеров. Скрытие данных – фундаментальная концепция объектно-ориентированного программирования, которая ограничивает доступ частных членов извне класса. Точно так же защищенные члены могут быть доступны только производным классам и недоступны извне. Например:
Однако в С++ есть функция, называемая дружественной, которая нарушает это правило и позволяет нам получать доступ к функциям-членам извне класса.
Точно так же есть дружественный класс, о котором мы узнаем позже в этом руководстве.
Что такое дружественная функция в C++?
Дружественная функция в C++ может получить доступ к личным и защищенным данным класса. Мы объявляем функцию, используя ключевое слово friend внутри тела класса.
Пример 1: работа функции
Здесь addFive() – это дружественная функция, которая может обращаться как к частным, так и к публичным элементам данных.
Хотя этот пример дает нам представление о концепции функции, он не показывает какого-либо значимого использования.
Более осмысленное использование будет работать с объектами двух разных классов. В этом случае функция friend может оказаться очень полезной.
Пример 2: добавление членов двух разных классов
В этой программе ClassA и ClassB объявили add(), как дружественную функцию. Таким образом, эта функция может получить доступ к приватным данным обоих классов.
Здесь следует отметить одну вещь: функция внутри ClassA использует ClassB. Однако мы еще не определили ClassB.
Чтобы это работало, нам нужно предварительное объявление ClassB в нашей программе.
Дружественный класс в C++
Мы также можем использовать Friend Class (дружественный класс) в С++, используя ключевое слово friend. Например:
Когда класс объявляется дружественным классом, все функции-члены дружественного класса становятся дружественными функциями.
Поскольку classB является дружественным классом, мы можем получить доступ ко всем членам classA изнутри classB.
Однако мы не можем получить доступ к членам ClassB изнутри classA. Это потому, что дружеские отношения в C++ только предоставляются, но не принимаются.
Пример 3
Вопросы по ООП C++
1)Минимально необходимое количество объектов при построении интерфейса?
Выберите один ответ:
a. Два объекта.
b. Четыре объекта.
c. Один объект.
d. Три объекта.
2)Какие утверждения верны относительно дружественных функций?
Выберите один или несколько ответов:
a. Дружественная функция может быть дружественна только одному классу.
b. Дружественная функция является членом класса.
c. Дружественная функция может быть дружественна более чем одному классу.
d. Дружественная функция не наследуется.
e. Дружественная функция может получить доступ к закрытым элементам класса только через объект данного класса.
3)В какой части описания класса определяется наследственность?
Выберите один ответ:
a. Можно определить либо в заголовочной части либо в части реализации.
b. Можно одновременно задать в обеих частях
c. В заголовочной части.
d. В части реализации.
4)Описание заголовка каких конструкторов принадлежит классу cloutput?
Выберите один или несколько ответов:
a. void cloutput ( );
b. cl_output ( );
c. Cloutput ( int i );
d. cloutput ( string s_name );
e. int cloutput ( int i );
f. cloutput ( );
g. clinput ( );
5)Где можно использовать указатель this?
Выберите один ответ:
a. В реализации любой функции.
b. При объявлении глобальных переменных
c. Только в реализации функции main;
d. В реализации любого метода класса
Вопросы по ООП C++
1)Сколько указателей могут указывать на один объект в рамках функции main? Выберите один ответ.
вопросы ООП
Есть вопрос. Если кто знает, о чем тут, отпишитесь: «Пример, отражающий четыре способа применения.
Вопросы по ООП С++
1)Правильно ли утверждение: алгоритм решения задачи системой содержит взаимодействия объектов.
ООП. Есть вопросы.
Собственно, обращаюсь к знающим людям, поскольку еще на первых порах с С++ не могу понять.
Когда вопросы кончаются, сделать кнопку неактивной и вывести сообщение о том, что вопросы кончились
Кто знает ребят подскажите в чем проблема, есть метод обновляющий текст в TextView (всего 6.
ООП ради ООП
Доброго времени суток! Есть к примеру класс Cat который реализует интерфейс Movable, инкапсулирует.
Вопросы
Ребята, никто не писал никогда код на С++, связанный с имитационным моделированием? Просто, дали.
Вопросы по Си
Здравствуйте! Начал изучать Си, и возникли кое-какие вопросы #ymstude void.
Вопросы по БД
Все привет! Начал как бы заниматься программированием и возникли некоторые вопросы с БД. Первый.
Дружественные функции
Дружественной функцией класса называется функция, которая, не являясь его компонентом, имеет доступ к его собственным (private) и защищенным (protected) компонентам. Функция не может стать другом класса «без его согласия». Для получения прав друга функция должна быть описана в теле класса со спецификатором friend. Именно при наличии такого описания класс предоставляет функции права доступа к защищенным и собственным компонентам.
Использование механизма дружественных функций позволяет упростить интерфейс между классами. Например, дружественная функция позволит получить доступ к собственным или защищенным компонентам сразу нескольких классов. Тем самым из классов можно иногда убрать компонентные функции, предназначенные только для доступа к этим «скрытым» компонентам.
В качестве примера рассмотрим дружественную функцию двух классов «точка на плоскости» и «прямая на плоскости».
В нижеописанной программе определены классы с общей дружественной функцией, в основной программе введены объекты этих классов и вычислено уклонение от точки до прямой:
Дружественная перегрузка
Дружественная перегрузка.Итак, мы рассмотрели дружественные функции и несколько примеров их применения. Однако одним из основных свойств этих специфических функций является то, что с их помощью можно осуществить перегрузку операторов. Такой тип перегрузки носит название дружественной.
Проиллюстрируем особенности оформления операции-функции в виде дружественной функции класса.
#include using namespace std;
класс реализующий работу с логическим значением class Flag <
Результат выполнения программы:
Допустим, переменные a и b объявлены как объекты класса C. В классе C определен оператор C::operator+(C), поэтому
a+b означает a.operator+(b)
Однако, также возможна глобальная перегрузка оператора +:
Такой вариант перегрузки тоже применим к выражению a+b, где a и b передаются соответственно в первом и втором параметрах функции. Из этих двух форм предпочтительной считается перегрузка в классе. Т. к. вторая форма требует открытого обращения к членам класса, а это отрицательно отражается на строгой эстетике инкапсуляции. Вторая форма может быть более удобной для адаптации классов, которые находятся в библиотеках, где исходный текст невозможно изменить и перекомпилировать.То есть добавить в класс перегрузку в качестве метода класса нереально.
Смешивать эти две формы в программе не рекомендуется. Если для некоторого оператора определены обе формы с одинаковыми типами формальных параметров, то использование оператора может создать двусмысленность, которая, скорее всего, окажется фатальной.
Тем не менее, глобальная перегрузка операторов обеспечивает симметрию, которая также обладает эстетической ценностью. Рассмотрим пример:
#include using namespace std;
класс «точка» class Point < координаты точки
глобальная перегрузка для ситуации int + Point доступ к private-членам через специальные функции Point&operator+(int d,Point&Z)<
Без глобальной перегрузки задача int + Point не решается. Поскольку мы не можем получить доступ к «родному” целому типу (то есть к типу int) и переопределить его операции, обеспечить симметрию простым определением операторов класса не удастся. Потребуется решение с глобальными функциями.
Для ввода информации из потока используется операция извлечения, которой является перегруженная операция сдвига вправо ». Левым операндом операции » является объект класса istream.
Чтобы избежать неожиданностей, ввод-вывод для абстрактных типов данных должен следовать тем же соглашениям, которые используются операциями ввода и вывода для встроенных типов, а именно:
1. Возвращаемым значением для операций ввода и вывода должна являться ссылка на поток, чтобы несколько операций могли быть выполнены в одном выражении.
3. Чтобы разрешить доступ к закрытым данным класса, операции ввода и вывода должны быть объявлены как дружественные функции класса.
4. В операцию вывода необходимо передавать константную ссылку на объект класса, поскольку данная операция не должна модифицировать выводимые объекты.
Итак, рассмотрим пример подобной перегрузки:
Проиллюстрируем особенности оформления операции-функции в виде дружественной функции класса.
Результат выполнения программы:
Глобальная перегрузка.
Допустим, переменные a и b объявлены как объекты класса C. В классе C определен оператор C::operator+(C), поэтому
Однако, также возможна глобальная перегрузка оператора +:
Такой вариант перегрузки тоже применим к выражению a+b, где a и b передаются соответственно в первом и втором параметрах функции. Из этих двух форм предпочтительной считается перегрузка в классе. Т. к. вторая форма требует открытого обращения к членам класса, а это отрицательно отражается на строгой эстетике инкапсуляции. Вторая форма может быть более удобной для адаптации классов, которые находятся в библиотеках, где исходный текст невозможно изменить и перекомпилировать.То есть добавить в класс перегрузку в качестве метода класса нереально.
Смешивать эти две формы в программе не рекомендуется. Если для некоторого оператора определены обе формы с одинаковыми типами формальных параметров, то использование оператора может создать двусмысленность, которая, скорее всего, окажется фатальной.
Тем не менее, глобальная перегрузка операторов обеспечивает симметрию, которая также обладает эстетической ценностью. Рассмотрим пример:
Без глобальной перегрузки задача int + Point не решается. Поскольку мы не можем получить доступ к «родному” целому типу (то есть к типу int) и переопределить его операции, обеспечить симметрию простым определением операторов класса не удастся. Потребуется решение с глобальными функциями.
Перегрузка ввода/вывода данных.
Для ввода информации из потока используется операция извлечения, которой является перегруженная операция сдвига вправо ». Левым операндом операции » является объект класса istream.
Чтобы избежать неожиданностей, ввод-вывод для абстрактных типов данных должен следовать тем же соглашениям, которые используются операциями ввода и вывода для встроенных типов, а именно:
Итак, рассмотрим пример подобной перегрузки:
Дружественные классы
Пора узнать, что «дружить» могут не только функции. Класс тоже может быть дружественным другому классу.
Друзья / FAQ C++
Это то, что позволяет вашему классу предоставлять доступ к себе другому классу или функции.
Друзья могут быть функциями или другими классами. Класс предоставляет своим друзьям права доступа. Обычно разработчик имеет политический и технический контроль и над друзьями, и над функциями-членами класса (в противном случае вам может потребоваться разрешение от владельца других частей, если вы хотите обновить свой собственный класс).
Нарушают ли друзья инкапсуляцию?
Нет! При правильном использовании они улучшают инкапсуляцию.
«Друг» – это явный механизм предоставления доступа, как и членство. Вы не можете (в стандартной программе) предоставить себе доступ к классу без изменения его источника. Например:
Описание модели защиты C++ смотрите в D&E (раздел 2.10) и TC++PL (разделы 11.5, 15.3 и C.11).
Часто бывает необходимо разделить класс пополам, если у этих двух половин будет разное количество экземпляров или разное время жизни. В этих случаях двум половинкам обычно требуется прямой доступ друг к другу (две половины раньше находились в одном классе, поэтому вы не увеличили объем кода, которому требуется прямой доступ к структуре данных; вы просто перетасовали код на два класса вместо одного). Самый безопасный способ реализовать это – подружить эти две половинки.
Точно так же, если вы используете дружественные функции как синтаксический вариант функций, доступных в секции public класса, они не нарушают инкапсуляцию больше, чем ее нарушает функция-член. Другими словами, друзья класса не нарушают барьер инкапсуляции: вместе с функциями-членами класса они являются барьером инкапсуляции.
(Многие люди думают о дружественной функции как о чем-то вне класса. Вместо этого попробуйте думать о дружественной функции как о части открытого интерфейса класса. Дружественная функция в объявлении класса нарушает инкапсуляцию не больше, чем ее нарушает публичная функция-член: обе имеют одинаковые права доступа к закрытым частям класса.)
Какие преимущества/недостатки есть у использования дружественных функций?
Они предоставляют некоторую свободу в вариантах проектирования интерфейса.
Что значит «дружба не передается по наследству, не является переходящей или взаимной»?
Тот факт, что я предоставляю вам дружественный доступ ко мне, не дает автоматически доступ ко мне вашим детям, не предоставляет автоматически доступ ко мне вашим друзьям и не предоставляет мне автоматически доступ к вам.
Что мне лучше объявлять в своем классе, функцию-член или дружественную функцию?
Используйте функцию-член, когда можете, и дружественную функцию, когда вам нужно.
Иногда друзья синтаксически лучше (например, в классе Fred дружественные функции позволяют параметру Fred быть вторым, в то время как функции-члены требуют, чтобы он был первым). Еще одно хорошее применение дружественных функций – это двоичные инфиксные арифметические операторы. Например, aComplex + aComplex должен быть определен как друг, а не как член, если вы хотите разрешить aFloat + aComplex (функции-члены не позволяют продвигать левый аргумент, так как это изменит класс объекта, который является получателем вызова функции-члена).
В остальных случаях выбирайте функцию-член вместо дружественной функции.
12.15 – Дружественные функции и классы
Большую часть этой главы мы проповедовали достоинства сохранения скрытости ваших данных. Однако иногда вы можете столкнуться с ситуациями, когда обнаружите, что у вас есть классы и функции вне этих классов, которые должны очень тесно работать друг с другом. Например, у вас может быть класс, в котором хранятся данные, и функция (или другой класс), которая отображает эти данные на экране. Хотя класс хранилища и код отображения были разделены для упрощения поддержки, код отображения на самом деле тесно связан с деталями класса хранилища. Следовательно, скрытие сведений о классах хранения от кода отображения не дает особой выгоды.
В подобных ситуациях есть два варианта:
Дружественные функции
Дружественная функция – это функция, которая может получить доступ к закрытым членам класса, как если бы она была членом этого класса. Во всем остальном дружественная функция похожа на обычную функцию. Дружественная функция может быть либо обычной функцией, либо функцией-членом другого класса. Чтобы объявить дружественную функцию, просто используйте ключевое слово friend перед прототипом функции, которую вы хотите сделать другом класса. Не имеет значения, объявляете ли вы дружественную функцию в закрытом или открытом разделе класса.
Вот пример использования дружественной функции:
Вот еще один пример:
Хотя оба приведенных выше примера довольно надуманы, последний пример очень похож на случаи, с которыми мы столкнемся позже, когда будем обсуждать перегрузку операторов!
Несколько друзей
Функция может быть другом для более чем одного класса одновременно. Например, рассмотрим следующий пример:
В этом примере стоит отметить две вещи. Во-первых, поскольку printWeather является другом обоих классов, она может получить доступ к закрытым данным из объектов обоих классов. Во-вторых, обратите внимание на следующую строку в верхней части примера:
Дружественные классы
Также целый класс можно сделать другом другого класса. Это дает всем членам дружественного класса доступ к закрытым членам другого класса. Вот пример:
Будьте осторожны при использовании дружественных функций и классов, потому что это позволяет дружественной функции или классу нарушать инкапсуляцию. Если детали реализации класса изменятся, то детали реализации друга также должны будут быть изменены. Следовательно, ограничьте использование дружественных функций и классов до минимума.
Дружественные функции-члены
Вместо того чтобы делать другом весь класс, вы можете сделать другом только одну функцию-член. Это делается аналогично тому, как сделать дружественной обычную функцию, за исключением использования имени функции-члена с включенным префиксом ИмяКласса:: (например, Display::displayItem ).
Однако на самом деле это может быть немного сложнее, чем ожидалось. Давайте, преобразуем предыдущий пример, чтобы сделать Display::displayItem дружественной функцией-членом. Вы можете попробовать сделать что-то вроде этого:
Вот как это выглядит:
Резюме
Дружественная функция или класс – это функция или класс, которые могут получить доступ к закрытым членам другого класса, как если бы они были членами этого класса. Это позволяет дружественной функции или классу тесно работать с другим классом, не заставляя другой класс открывать свои закрытые члены (например, через функции доступа).
Объявление друзьями обычно используется при определении перегруженных операторов (о которых мы поговорим в следующей главе) или, реже, когда два или более класса должны тесно взаимодействовать друг с другом.
Обратите внимание, что для того, чтобы сделать конкретную функцию-член другом, необходимо сначала увидеть полное определение класса функции-члена.
Небольшой тест
Вопрос 1
В геометрии точка – это позиция в пространстве. Мы можем определить точку в трехмерном пространстве как набор координат x, y и z. Например, Point(2.0, 1.0, 0.0) будет точкой в пространстве с координатами x = 2.0, y = 1.0 и z = 0.0.
В физике вектор – это величина, которая имеет величину (длину) и направление (но не положение). Мы можем определить вектор в трехмерном пространстве как значения x, y и z, представляющие направление вектора вдоль осей x, y и z (длина может быть получена из них). Например, Vector(2.0, 0.0, 0.0) будет вектором, представляющим направление вдоль (только) положительной оси x, с длиной 2.0.
Вектор можно применить к точке, чтобы переместить точку в новое положение. Это делается путем добавления направления вектора к положению точки, чтобы получить новое положение. Например, Point(2.0, 1.0, 0.0) + Vector(2.0, 0.0, 0.0) даст точку (4.0, 1.0, 0.0).
Точки и векторы часто используются в компьютерной графике (точки представляют вершины фигуры, а векторы представляют движение фигуры).
Учитывая следующую программу: