Дружественные функции и классы
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 32 из 104.
Иногда функции или классу за пределами вашего класса нужен доступ к его закрытым членам. Вместо того чтобы делать эти члены общедоступными, C++ предоставляет ключевое слово friend, позволяющее предоставить выборочный доступ, сохраняя инкапсуляцию.
friend function объявляется внутри class, но не является членом этого class. Она может получать доступ к закрытым и защищённым членам class:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Объявить printWidth как friend
friend void printWidth(const Box& b);
};
// Определение friend-функции — НЕ функция-член
void printWidth(const Box& b) {
std::cout << b.width; // Может получить доступ к private-члену
}Обратите внимание, что printWidth определена вне класса без оператора разрешения области видимости. Это обычная function, у которой случайно есть специальные права доступа.
Вы также можете сделать целый class friend, предоставив всем его member function access к закрытым member:
class Engine {
int horsepower;
friend class Mechanic; // Mechanic может получать доступ к приватным членам
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Разрешено - Mechanic является другом
}
};Дружба не является взаимной: если Engine объявляет Mechanic как friend, Mechanic может получать доступ к закрытым members Engine, но не наоборот. Используй friend умеренно, поскольку он создаёт тесную связанность между классами. Чаще всего friend полезен для перегрузки operator и тесно связанных вспомогательных function.
Задание
ЛегкоДавайте создадим безопасную систему обмена сообщениями, в которой MessageReader class нужен специальный доступ для чтения зашифрованных сообщений, хранящихся в SecureMessage class. Это идеальный сценарий для использования friend functions и friend classes.
Вы создадите два файла, чтобы организовать свой код:
SecureMessage.h: DefineSecureMessageclass, который хранит private данные сообщения и предоставляет выборочный доступ доверенным сущностям. Ваш class должен содержать:- Private members:
content(string) иsecretKey(int) - constructor, принимающий content и secret key
- Declare
MessageReaderкак friend class, чтобы он мог обращаться к private members - Declare friend function
decryptMessage(const SecureMessage& msg, int key), которая возвращает content только в том случае, если переданный key совпадает с secret key, иначе возвращает"Access Denied"
Затем define
MessageReaderclass в том же header-файле:- method
readContent(const SecureMessage& msg), который напрямую обращается к privatecontentсообщения и возвращает его - method
verifyKey(const SecureMessage& msg, int key), который проверяет, совпадает ли переданный key сsecretKeyсообщения, и возвращает"Valid"или"Invalid"
Наконец, define friend function
decryptMessageпосле classes.- Private members:
main.cpp: Продемонстрируйте работу friend functions и friend classes. Прочитайте из input content сообщения, secret key и test key (каждый с отдельной строки), затем:- Create
SecureMessageс content и secret key - Create
MessageReaderobject - Выведите
"Reader access: <content>"с помощью methodreadContent()reader - Выведите
"Key verification: <result>"с помощьюverifyKey()и test key - Выведите
"Decrypt with test key: <result>"с помощью friend function и test key - Выведите
"Decrypt with correct key: <result>"с помощью friend function и исходного secret key
- Create
Помните, что friend functions определяются за пределами class без оператора разрешения области видимости: это обычные functions с особыми правами доступа. MessageReader class, являясь friend, может напрямую обращаться к private members SecureMessage во всех своих methods.
Преобразуйте key, полученные из input, из строк в целые числа с помощью std::stoi(). Подключите header-файл в main.cpp с помощью #include "SecureMessage.h".
Попробуйте сами
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// Читать ввод
string messageContent;
string secretKeyStr;
string testKeyStr;
getline(cin, messageContent);
getline(cin, secretKeyStr);
getline(cin, testKeyStr);
int secretKey = stoi(secretKeyStr);
int testKey = stoi(testKeyStr);
// TODO: Создать SecureMessage с содержимым и секретным ключом
// TODO: Создать объект MessageReader
// TODO: Print "Reader access: <content>" using readContent()
// TODO: Print "Key verification: <result>" using verifyKey() with test key
// TODO: Print "Decrypt with test key: <result>" using decryptMessage with test key
// TODO: Print "Decrypt with correct key: <result>" using decryptMessage with secret key
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++