
- •1. Понятие класса. Методы класса. Управление доступом к компонентам.
- •2. Объявление и определение класса. Внешнее определение функций.
- •3. Создание, копирование и удаление объекта.
- •4. Статические компоненты класса. Инициализация статических компонентов класса.
- •5. Наследование. Типы наследования. Виртуальное наследование.
- •6. Виртуальные функции
- •7. Абстрактные классы и чистые виртуальные функции. Интерфейс
- •8. Дружественность. Дружественные классы и функции.
- •9. Вложенные классы. Внутреннее и внешнее определение.
- •10. Шаблоны классов
- •11.Создание экземпляров шаблона. Инстанцирование.
- •12.Шаблоны и наследование.
- •13.Терминология шаблонов.
- •14. Параметры и аргументы шаблона.
- •15. Шаблоны компонентных функций
- •16. Полная специализация шаблонов.
- •17. Частичная специализация шаблонов.
- •18. Перегрузка операций. Основные понятия.
- •19. Перегрузка унарных операций.
- •20. Перегрузка бинарных операций.
- •Int test() {
- •Int test() {
- •Int test() {
- •// Делаем что-то
- •Вопрос 23
- •24. Группировка и композиция исключений. Повторная генерация. Перехват всех исключений.
- •25. Автоматическое управление ресурсами. Методика raii.
- •Void f() { FileOpen("myfile.Txt", "rt"); //здесь выполняем нужную работу с файлом //... }
- •Void f (int a) throw (x2, x3)
- •27. Стандартная библиотека. Организация стандартной библиотеки
- •28. Тип вектора. Вложенные типы. Итераторы. Доступ к элементам
- •29.Тип Вектора. Конструкторы. Операции со стеком. Списочные операции. Размеры и емкость.
- •30. Стандартные контейнеры. Вопросы производительности операций.
- •31. Процесс разработки по. Цели и этапы проектирования.
- •32. Процесс разработки по. Выявление классов. Определение операций.
- •33. Процесс разработки по. Определение взаимосвязей. Определение интерфейсов.
- •Этап 3: выявление зависимостей
- •Этап 4: определение интерфейсов
- •34. Паттерны проектирования. Основные паттерны.
- •35. Тестирование по. Методы тестирования.
8. Дружественность. Дружественные классы и функции.
Спецификаторы доступа класса позволяют указывать, могут ли функции вне определенного вами класса обращаться к его элементам. Может, однако, случиться, что вам потребуется обеспечить определенной функции или классу доступ к элементам вашего класса, специфицированным как private или protected. Для обеспечения такой возможности используется ключевое слово friend. Вы можете разрешить элементам другого класса (anotherClass) полный доступ к элементам вашего класса (myClass), объявленным как private или protected, включив в определение вашего класса описание friend. Например:
class myClass { friend class anotherClass; };
Аналогично вы можете разрешить обычной функции или функции-элементу другого класса полный доступ к элементам класса с помощью описания friend. Например:
class myClass { friend void anotherClass::MemberFuncName(int); friend void regularFuncName(double); };
К друзьям и дружественности применимы следующие правила: • на описания friend не влияют спецификаторы public, protected или private; • описания friend не взаимны: если А объявляет В другом, то это не означает, что А является другом для В; • дружественность не наследуется: если А объявляет В другом, классы, производные от В, не будут автоматически получать доступ к элементам А; • дружественность не является переходным свойством: если А объявляет В другом, классы, производные от А, не будут автоматически признавать дружественность В. Обычное объявление функции-члена гарантирует три логически разные вещи: • во-первых, функция имеет право доступа к закрытой части объявления класса; • во-вторых, функция находится в области видимости класса; • в-третьих, функция должна вызываться для объекта класса, то есть имеется указатель this, Объявив функцию-член как static, мы придаем ей только первые два свойства. Объявив функцию как friend, мы наделяем ее только первым свойством. Так же как и функции-члены, функции-друзья явно указываются в объявлении класса, друзьями которого они являются. Поэтому они в той же мере являются частью интерфейса класса, в какой ею являются функции-члены. Так же как и объявление члена, объявление friend не добавляет новое имя в охватывающую область видимости. Например:
class Matrix { friend class Xform; friend Matrix invert (const Matrix&); … }; Xform x; /* ошибка: в текущей области видимости нет имени Xform */ Matrix (*p)(const Matrix&) = &invert; /* ошибка: в текущей области видимости нет имени invert */ Класс-друг должен быть предварительно объявлен в охватывающей области видимости или определен в области видимости, непосредственно охватывающей класс, объявивший его другом. При этом не принимаются во внимание области видимости вне области видимости самого внутреннего охватывающего пространства имен. Например:
class AE {…}; // не друг класса Y namespace N { class X {…}; // друг класса Y class Y { friend class X; friend class Z; friend class AE; }; class Z {…}; // друг класса Y }; Функцию-друга можно явно объявить точно так же, как и класс-друг, или ее поиск осуществляется по типам ее аргументов так, как будто она была объ-явлена вне класса, но в области видимости, непосредственно охватывающей класс. Например:
void f(Matrix &m) { invert(m); // функция invert – друг класса Matrix } Из этого следует, что функция-друг класса должна быть либо явно объявлена в охватывающей области видимости, либо иметь аргументы этого класса. В противном случае функцию-друга вызывать нельзя. Например:
class X { friend void f(); /* бесполезно, т.к. в этой области видимости нет имени f()*/ friend void h(const X&); /* можно найти по типу ар-гумента */ }; void g(const X &x) { f(); // нет имени f() в области видимости h(x); // функция h(x) – друг класса X }
Члены класса-члена не имеют свободного доступа к членам внешнего класса. Аналогично члены внешнего класса не имеют свободного доступа к членам вложенного класса; нужно соблюдать обычные правила доступа. Например:
class Outer //внешний { typedef int T; int i; public: int i2; static int s; class Inner // внутренний { int x; T y; // ошибка: Outer::T – закрытый public: void f(Outer *p, int v); }; int g(Inner *p); }; void Outer::Inner::f(Outer *p, int v) { p->i = v; // ошибка: Outer::i – закрытый p->i2 = v; // правильно: Outer::i2 – открытый } int Outer::g(Outer *p) { p->f(this, 2); // правильно: Inner::f – открытый return p->x; // ошибка: Inner::x – закрытый }
Однако часто полезно позволить классу-члену обращаться к его внешнему классу. Этого можно добиться, сделав член другом. Например:
class Outer { typedef int T; int i; public: class Inner; /* предварительное объявление класса члена */ friend class Inner; /* разрешаем доступ к Outr::Inner */ class Inner { int x; T y; // правильно: Inner – друг public: void f(Outer *p, int v); }; }; void Outer::Inner::f(Outer *p, int v) { p->i = v; // правильно: Inner – друг }