
- •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. Тестирование по. Методы тестирования.
7. Абстрактные классы и чистые виртуальные функции. Интерфейс
Расширим пример текстового файла. Предположим, что нам нужно сделать для класса TextFile базовый класс File, от которого будет унаследован еще один класс RTFFile. Однако, в такой ситуации неизвестно как реализовать метод read() класса File, т.к. класс File не реализует поведение какого-то конкретного типа файлов, а представляет интерфейс для работы с различными файлами. В этом случае, метод read(...) этого класса нужно сделать чистым виртуальным, дописав "= 0" после его сигнатуры:
struct File { virtual string read(size_t count) = 0; };
Это означает, что метод read(...) должен быть определен в классах наследниках. Теперь класс File стал абстрактным, и его экземпляры невозможно создать. Но зато можно работать через указатель на абстрактный класс с объектами производных классов, например, так:
File *f = new TextFile("text.txt"); //различные действия с файлом text.txt delete f; f = new RTFFile("rich_text.rtf"); //различные действия с файлом rich_text.rtf delete f;
Следует отметить, что в любой иерархии классов деструктор всегда должен быть виртуальным. Рассмотрим пример, поясняющий важность этого факта:
struct Person { public: ~Person() {} private: string name; //... }; struct Student : Person { public: Student() { someData = new Data(); } ~Student() { delete someData; } //... private: Data *someData; }; //... Student *s = new Student(); //... delete s; //вызовется деструктор класса Student, память по указателю someData освободится Person *p = new Student(); //... delete p; /*вызовется деструктор класса Person, а не Student, т.к. он не является виртуальным, несмотря на то, что на самом деле объект - экземпляр Student. В этом случае произойдет утечка памяти, т.к. память по указателю someData не освободится */
Деструктор можно также сделать чистым виртуальным, но при этом его тело нужно определить снаружи класса.
Интерфейс - это класс, в методах которого отсутствуют реализации, в результате чего он не может иметь экземпляров, но может наследоваться (абстрактный класс).
Интерфейс можно представить как элемент управления между программистом и кодом, абстрагируя тонкости реализации методов от самого класса. По большому счету, интерфейс это родительский класс с перечислением общих (по отношению к дочерним классам) методов. Пример: есть класс Object. У него есть метод Draw. Метод должен рисовать объект.
class Object { public: Object()}; void Draw(){}; };
Наследуем от Object новый класс - Triangle. В нем реализуем метод таким образом, чтобы рисовался треугольник.
class Triangle : public Object { public: Triangle(){}; void Draw() { printf("рисуется треугольник"); }; };
Наследуем от Object класс Plane, метод Draw в нем реализуем таким образом, чтобы рисовалась плоскость.
class Plane : public Object { public: Plane(){}; void Draw() { printf("рисуется плоскость"); }; };
Почему нельзя просто объявить и реализовать метод Draw без всякого наследования? Потому что при данном подходе программист будет знать: раз объект наследуется от класса Object, то у него 100% есть метод Draw. Таким образом очень удобно объединять схожие по функционалу классы. Как бы в последствии не менялись реализации методов, программист может спокойно использовать метод Draw для отрисовки любого объекта.
Абстрактный класс является классом, который может использоваться только в качестве базового для других классов. Абстрактный класс содержит одну или несколько чистых виртуальных функций. Чистая виртуальная функция может рассматриваться как встроенная функция, тело которой определено как =0 (чистый спецификатор). Для чистой виртуальной функции не нужно приводить действительное определение; предполагается, что она переопределяется в производных классах. К абстрактным классам применимы следующие правила: • абстрактный класс не может использоваться в качестве типа аргумента функции или типа возвращаемого значения; • абстрактный класс нельзя использовать в явном преобразовании; • нельзя определить представитель абстрактного класса (локальную/глобальную переменную или элемент данных); • можно определять указатель или ссылку на абстрактный класс; • если класс, производный от абстрактного, не определяет все чистые виртуальные функции абстрактного класса, он также является абстрактным.