Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
для шпор(печатаем 8 стр на листе).docx
Скачиваний:
2
Добавлен:
01.04.2025
Размер:
2.86 Mб
Скачать

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 (чистый спецификатор). Для чистой виртуальной функции не нужно приводить действительное определение; предполагается, что она переопределяется в производных классах. К абстрактным классам применимы следующие правила:абстрактный класс не может использоваться в качестве типа аргумента функции или типа возвращаемого значения; • абстрактный класс нельзя использовать в явном преобразовании; • нельзя определить представитель абстрактного класса (локальную/глобальную переменную или элемент данных); • можно определять указатель или ссылку на абстрактный класс; • если класс, производный от абстрактного, не определяет все чистые виртуальные функции абстрактного класса, он также является абстрактным.