C++.Бархатный путь


Друзья класса


Три спецификатора доступа обеспечивают в C++ управление доступом. Эти спецификаторы являются основанием принципа инкапсуляции - одного из трёх основных принципов объектно-ориентированного программирования. Соблюдение правил доступа повышает надёжность программного обеспечения.

Спецификаторы доступа способны обеспечить многоуровневую защиту функций и данных в наследуемых классах. Порождаемые на основе "инкапсулированных" классов объекты способны поддерживать жёсткий интерфейс. Они подобны "чёрным" ящикам с чётко обозначенными входами и выходами. Вместе с тем, следует признать, что система управления доступом, реализованная на основе трёх спецификаторов, не является гибкой. С её помощью может быть реализована защита по принципу "допускать ВСЕХ (члены класса, объявленные в секции public) или не допускать НИКОГО (члены класса, объявленные в секциях protected и private)". В C++ существует возможность организации более гибкой защиты. Здесь можно также объявлять функции, отдельные функции-члены классов и даже классы (в этом случае речь идёт о полном множестве функций-членов класса), которые получают доступ к защищённым и приватным членам данного класса. Что означает реализацию системы управления доступом принципу "не допускать НИКОГО, КРОМЕ". Такие функции и классы называют дружественными функциями и классами. Объявление дружественных классов и функций включается в объявление данного класса вместе со спецификатором объявления friend. Здесь нам потребуется всего одна форма Бэкуса-Наура для того, чтобы дополнить синтаксис объявления.

СпецификаторОбъявления ::= friend ::= *****

Рассмотрим небольшой пример использования дружественных функций и классов, а затем сформулируем основные правила работы с друзьями классов. В программе объявлены два класса, один из которых является другом другого класса и всеобщая дружественная функция.

#include <iostream.h> class XXX; /* Неполное объявление класса. Оно необходимо для объявления типа параметра функции-члена для следующего класса. */ class MMM { private: int m1; public: MMM(int val); void TypeVal(char *ObjectName, XXX& ClassParam); }; MMM::MMM(int val) { m1 = val; } /* Определение функции-члена TypeVal располагается после объявления класса XXX. Только тогда транслятор узнаёт о структуре класса, к которому должна получить доступ функция MMM::TypeVal. */ class XXX { friend class YYY; friend void MMM::TypeVal(char *ObjectName, XXX& ClassParam); friend void TypeVal(XXX& ClassParamX, YYY& ClassParamY); /* В классе объявляются три друга данного класса: класс YYY, функция-член класса MMM, простая функция TypeVal. В класс XXX включаются лишь объявления дружественных функций и классов. Все определения располагаются в других местах - там, где им и положено быть - в своих собственных областях видимости. */ private: int x1; public: XXX(int val); }; XXX::XXX(int val) { x1 = val; } void MMM::TypeVal(char *ObjectName, XXX& ClassParam) { cout << "Значение " << ObjectName << ": " << ClassParam.x1 << endl; } /* Отложенное определение функции-члена MMM::TypeVal. */ class YYY { friend void TypeVal(XXX& ClassParamX, YYY& ClassParamY); private: int y1; public: YYY(int val); void TypeVal(char *ObjectName, XXX& ClassParam); }; YYY::YYY(int val) { y1 = val; } void YYY::TypeVal(char *ObjectName, XXX& ClassParam) { cout << "Значение " << ObjectName << ": " << ClassParam.x1 << endl; } void TypeVal(XXX& ClassParamX, YYY& ClassParamY); void main() { XXX mem1(1); XXX mem2(2); XXX mem3(3); YYY disp1(1); YYY disp2(2); MMM special(0); disp1.TypeVal("mem1", mem1); disp2.TypeVal("mem2", mem2); disp2.TypeVal("mem3", mem3); special.TypeVal("\n mem2 from special spy:", mem2); TypeVal(mem1, disp2); TypeVal(mem2, disp1); } void TypeVal(XXX& ClassParamX, YYY& ClassParamY) { cout << endl; cout << "???.x1 == " << ClassParamX.x1 << endl; cout << "???.y1 == " << ClassParamY.y1 << endl; }




- Начало -  - Назад -  - Вперед -