Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 88 89 90 [ 91 ] 92 93 94 ... 144


Клосс, где есть хотя бы одно исключительно виртуальная функция, нозывоется абстрактным. Его можно использовоть только в качестве бозового для какого-то другого классо. В частности, нельзя создовать объекты абстрактного класса. Класс, производный от абстрактного, должен либо содержать определения исключительно виртуольных функций, либо снова объявить их такими.

Концепции исключительно виртуольных функций было отдано предпочтение по сравнению с явным объявлением класса абстрактным, поскольку выборочное определение функций окозы-воется номного более гибким .

Как показано выше, в С-ы- и раньше была возможность выразить понятие абстрактного класса, но для этого требовалось проделать больше работы, чем хотелось бы. Некоторые пользователи считали это важным вопросом (см. например, [Johnson, 1989]). Но лишь за несколько недель до выпуска версии 2.0 я осознал, что очень немногие вообще понимают эту концепцию и что непонимание сути абстрактных классов было источником многих ошибок проектирования.

13.2.2. Абстрактные типы

Типичной претензией к С++ было (и остается) то, что закрытые данные включаются в объявление класса. Поэтому при изменении закрытых данных приходится перекомпилировать весь код, в котором данный класс используется. Часто недовольство по этому поводу формулировали так: абстрактные типы в языке С++ не являются по-настоящему абстрактными или данные на самом деле не являются скрытыми . Многие пользователи думали, что уж если они могут поместить представление объекта в закрытую секцию класса, то обязательно должны так поступить. Разумеется, это неправильно (поэтому-то данная проблема многие годы и ускользала от моего внимания). Если вы не хотите помещать представление в класс, так и пе надо! Просто перенесите спецификацию представления в какой-то производный класс. Нотация абстрактных классов позволяет сделать это явно. Например, можно так определить множество указателей типа Т:

class set { public:

virtual void insert(T*) =0;

virtual void remove(T*) =0;

virtual int is meraber(T*) =0;

virtual T* first 0 =0; virtual T* nextO =0;

virtual -setO { }

Здесь есть вся информация, необходимая для использования класса set, а также отсутствуют детали представления или реализации. Только тот, кто действительно создает объекты классов set, должен знать, как представлено множество. Например, если есть объявление

class slist set : public set, private slist ( slink* current elem;



public:

void insert(Т*); void remove(T*);

int is member(T*);

T* first 0 ; T* next();

slist set() : slistO, current elem(0) { }

to можно создать объекты slist set, с которыми смогут работать пользователи класса set, даже не подозревающие ни о каком slist set. Например:

void userl(set& s) {

for (Т* p = s.firstO; p; p=s.next()) { использовать p

void user2() {

slist set ss; . . . userl(ss);

Важно, что функцию, в которой используется абстрактный класс set, например userl (), можно откомпилировать, не включая заголовочные файлы, содержащие определение slist set и классы типа slist, от которых последнее зависит.

Как уже отмечено выше, компилятор не позволяет создать объект абстрактного класса. Например:

void f(set& si) правильно {

set s2; ошибка: объявление объекта

абстрактного класса set set* р = 0; правильно set& s3 = si; правильно

Концепция абстрактных классов важна пото.му, что позволяет провести более четкую границу между пользователем и разработчиком реализации. Абстрактный класс - интерфейс к реализациям, содержащимся в производных от него классах. Это уменьшает число перекомпиляций после изменений, равно как и объем информации, необходимой для компиляции обычного фрагмента кода. С помощью абстрактных классов уменьшается зависимость пользователя от разработчика, снимаются претензии тех, кто жалуется на долгую компиляцию. Данное средство



заодно служит и интересам поставщиков библиотек, которым больше не нужно беспокоиться, как изменения реализации отразятся на работе пользователей. Я видел системы, время компиляции которых уменьшилось на порядок после того, как основные интерфейсы были реализованы в виде абстрактных классов. Попытка объяснить все это в [Stroustrup, 1986b] оказалась безуспешной, и лишь после появления в языке явной поддержки для абстрактных классов моя .мысль стала понятной пользователям [2nd].

13.2.3. Синтаксис

Странный синтаксис =0 был выбран вместо очевидной альтернативы ввести ключевое слово pure или abstract только потому, что тогда я не видел возможности добавить новое ключевое слово. Если бы я предложил pure, то версия 2.0 вышла бы без абстрактных классов. Чтобы не рисковать сроками выпуска версии и не навлекать на себя упреков по поводу нового ключевого слова, я воспользовался традиционным для С и C-i-i- соглашением о том, что О означает отсутствует . Синтаксис = О не противоречит моим взглядам на тело функции как на инициализатор функции, а также представлениям (упрощенны.м, но обычно соответствующим реальности) о том, что множество виртуальных функций реализовано в виде вектора указателей на функции (см. раздел 3.5.1). На само.м деле, реализация синтаксиса = 0 путем помещения О в vtbl - не лучший способ. Я помещал в vtbl

указатель на функцию pure virtual called; затем эту функцию мож1ю

было определить так, чтобы она выводила подходящее сообщение об ошибке во время выполнения.

Лучше объявлять отдельные функции исключительно виртуальными, а не весь класс - абстрактным, поскольку в этом случае удается добиться большей гибкости. Я высоко ценю возможность определять класс поэтапно, то есть часть виртуальных функций определить в базовом классе, а остальные - в производных.

13.2.4. Виртуальные функции и конструкторы

Конструирование объекта из базовых классов и членов (см. раздел 2.11.1) влияет на работу механизма виртуальных функций. Подчас по этому поводу возникала путаница. Поэтому здесь объясняется, почему избранный для С++ путь является почти безальтернативным.

13.2.4.1. Вызов исклизчитвльно виртуальной функции

Как вообще можно вызвать чисто виртуальную функцию, а не замещающую ее в одном из производных классов? Объекты абстрактных классов могут существовать только в качестве базовых для каких-то других классов. После того как объект производного класса сконструирован, наша виртуальная функция уже замещена какой-то другой, определенной в производном классе. Однако в процессе конструирования собственный конструктор абстрактного класса может по опшбке вызвать исключительно виртуальную функцию:

class А { public:

virtual void f() =0;



1 ... 88 89 90 [ 91 ] 92 93 94 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика