46019 (665326), страница 21
Текст из файла (страница 21)
class shape (* // абстрактный класс
point center;
...
public:
where() (* return center; *)
move(point p) (* center = p; draw(); *)
virtual void rotate(int) = 0; // чистая виртуальная функция
virtual void draw() = 0; // чистая виртуальная функция
virtual void hilite() = 0; // чистая виртуальная функция
...
*)
shape x; // ошибка: попытка создания объекта абстрактного
// класса
shape* sptr; // указатель на абстрактный класс допустим
shape f(); // ошибка: абстрактный класс не может являться
// типом возврата
int q(shape s); // ошибка: абстрактный класс не может являться
// типом аргумента функции
shape& h(shape&);// ссылка на абстрактный класс в качестве типа
// возврата или аргумента функции допустим
Предположим, что D является производным классом непосредственно от абстрактного базового класса B. Тогда для каждой чистой виртуальной функции pvf в B либо D должен обеспечивать определение pvf, либоD должен объявлять pvf как чистую функцию.
Например, с использованием показанного выше класса shape:
class circle : public shape (* // circle является производным // от абстрактного класса
int radius; // private
public:
void rotate(int) (* *) // определяется виртуальная
// функция:
// действия по вращению окруж-
// ности отсутствуют
void draw(); // circle::draw должна быть
// где-либо определена
void hilite() = 0; // переопределение функции
// как "чистой"
Функций-компоненты могут быть объявлены из конструктора абстрактногокласса, но вызов чистой виртуальной функции непосредственно или косвенно из такого конструктораприводит к ошибке времени выполнения.
Контекст С++
Лексические правилаопределения контекста в С++, безотносительно к контексту класса, следуют общим правилам С, но с учетом того, что С++, в отличие от С, позволяет объявлениякак данных, так и функций везде, где может находиться оператор. Такая гибкость означает, что необходима осторожность при интерпретации таких фраз, как "объемлющий контекст" и "точка объявления".
Контекст класса
Имя М компонента класса Х имеет контекст класса "локальный по отношению Х"; оно может использоваться в следующих ситуациях:
- в функциях-компонентах Х
- в выражениях типа x.M, где x есть объект Х
- в выражениях типа xptr->M, где xptr есть указатель объекта Х
- в выражениях типа X::M или D::M, где D есть производный класс от X
- в ссылках вперед в пределах класса, которому принадлежит компонент
Классы, перечислимые данные или имена typedef, объявленные в пределах класса Х, либо имена функций, объявленные как "друзья" Х, не являются компонентами Х; их имена просто имеют объемлющий контекст.
Скрытые имена
Имя может бытьскрытоявным объявлением того же имени в объемлющем блоке или в классе. Скрытый компонент класса тем не менее остается доступным при помощи модификатора контекста, заданного с именем класса X:M. Скрытое имя с контекстом файла (глобальное) позволяет ссылку на него при помощи унарной операции ::, например ::g. Имя класса Х может быть скрыто именем объекта, функции, либо нумератора, заданного в контексте Х, независимо от последовательности объявления имен. Однако, имя скрытого класса Х доступно прииспользовании префикса Х с соответствующим ключевым словом class, struct или union.
Точка объявления имени х находится непосредственно после его полного объявления, но до инициализатора, если таковой существует.
Краткое изложение правил определения контекста С++
Следующие правила применимы ко всем именам, включая имена typedef и имена классов, при условии, что то или иноеимя допустимо в С++ в конкретном обсуждаемом контексте:
1. Сначала само имя проверяется на наличие неоднозначностей. Если в пределах контекста неоднозначности отсутствуют, то инициируется последовательность доступа.
2. Если ошибок управления доступа не обнаружено, то проверяется тип объекта, функции, класса, typedef, и т.д.
3. Если имя используется вне какой-либо функции или класса, либо имеет префикс унарной операции контекста доступа ::, и если имя не квалифицировано бинарной операцией ::или операциямивыборакомпонента . или ->, то это имя должно быть именем глобального объекта, функции или нумератора.
4. Если имя n появляется в одном из видов: X::n, x.n (где x это объект класса Х или ссылка на Х), либо ptr->n (где ptrэто указатель на Х),то n является именем компонента Хили компонентом класса, производным от которого является Х.
5. Любое до сих пор не рассмотренное имя, используемое в качестве статической функции-компонента, должно быть объявлено в блоке, в которомоно встречается, либо в объемлющемблоке, либо являться глобальным именем. Объявление локального имени n скрывает объявления n в объемлющих блоках и глобальные объявления n. Имена в различныхконтекстах немогут быть перегружены.
6. Любое не рассмотренное до сих пор имя, используемое в качестве имени нестатической функции компонента класса Х, должно быть объявлено в блоке, в котором оно встречается, либо в объемлющем блоке, быть компонентом класса Х или базового относительно Х класса, либо быть глобальным именем. Объявление локального имени n скрывает объявления n в объемлющих блоках,компонентах класса- функциях и глобальных объявлениях n. Объявление имени компонента скрывает объявления этого имени в базовых классах.
7. Имя аргумента функции в определении функции находится в контексте самого внешнего блока данной функции. Имя аргумента функции в не-определяющем объявлении функции вообще не имеет контекста. Контекст аргумента по умолчанию объявляется точкой объявления данного аргумента, ноне позволяет доступ к локальным переменным или не-статическим компонентам класса. Аргументы по умолчанию вычисляются в каждой точке вызова.
8. Инициализатор конструктора (см. "инициализатор-конструктора" в описании синтаксиса декларатора класса в таблице
1.12 на стр.37 оригинала) вычисляется в контексте самого внешнего блока конструктора, и поэтому разрешает ссылки на имена аргументов конструктора.
Директивы препроцессора Turbo C++
Хотя Turbo C++ использует использует интегрированный однопроходный компилятор как при работе в интегрированной среде разработки (IDE), так и при вызове компилятора из командной строки, полезно тем не менее сохранить терминологию, сохранившуюся от более ранних, многопроходных компиляторов. В случае последних на первом проходе обработки исходного текста программы выполняется подключение всех имеющихсявключаемых файлов, проверка всех условных директив компиляции, расширение всех имеющихся макросов и получение промежуточного файла для обработки последующими проходами компилятора. Посколькукак интегрированная среда разработки, так и версия командной строки Turbo C++ выполняют первый проход, не создаваяпри этом каких-либо промежуточных файлов, Turbo C++ включаетв себя независимый препроцессор, CPP.EXE, который имеет на выходе такой промежуточный файл. CPP полезен на стадии отладки, посколькупоказывает в чистом виде результаты работы директив включения, условных директив компиляции и сложных макрорасширений.
CPP позволяет обращение к документации по нему в диалоговом режиме.
Следующее обсуждение директив препроцессора, их синтаксис и семантика, применимы, следовательно, как к самому препроцессору CPP, так и к его функциям, встроенным в компиляторы Turbo C++.
Препроцессор находит директивы препроцессора (которые называют также управляющимистроками препроцессора) и выполняет лексический анализ находящихся в них фраз.
Препроцессор Turbo C++ включаетв себя сложный процессор макросов, сканирующий исходный код перед обработкойего компилятором.Препроцессор обеспечивает мощные средства и гибкость, заключающиеся в следующем:
- Определение макросов, которые служат для снижения трудоемкости программирования и улучшении читаемости кода. Некоторые макросы позволяют избежать затрат на вызов функций.
- Включение текстов из других файлов, таких как файлы заголовка, в которых содержатся прототипы стандартных библиотечных и определяемых пользователем функций, а также буквальные константы.
- Установка условной компиляции для улучшения мобильности получаемых кодов и для целей отладки.
Директивы препроцессора обычно помещаются в начало исходного кода, но допустимы в любой точке программы.
Любая строка с ведущим символом # рассматриваетсякак директива препроцессора, еслитолько# не входит в строковый литерал, символьную константу или комментарий. Ведущему символу # может предшествовать, либо следовать за ним,пробельные символы (за исключением символа новой строки).
Полный синтаксис препроцессора Turbo C++ показан в следующей таблице:
Синтаксис директив препроцессора Turbo C++ Таблица 1.23
файл-для-препроцессора:
группа
группа:
часть группы
группа часть-группы
часть-группы:
новая-строка
if-раздел
управляющая строка
if-раздел:
if-группа endif-строка
if-группа:
#if выражение-типа-константы новая-строка
#ifdef идентификатор новая-строка
#ifndef идентификатор новая-строка
elif-группы:
elif-группа
elif-группы elif-группа
elif-группа:
#elif выражение-типа-константы
else-группа:
#else новая-строка
endif-строка:
#endif новая-строка
управляющая-строка:
#include лексемы-препроцессора новая-строка
#define идентификатор список-замены новая-строка
#define идентификатор левая-круглая-скобка
) список-замены новая-строка
#undef идентификатор новая-строка
#line новая-строка
#pragma новая-строка
#pragma warn действие сокращение новая-строка
#pragma inline новая-строка
? новая-строка
действие: одно из
+ - .
сокращение:
amb ampapt aus big cincpt
def dupelf mod par piapro
rch retrng rpt rvf sigstr
stu stvsus ucp use volzst
левая-круглая-скобка:
символ левой круглой скобки без предшествующих пробельных символов
список-замены:
лексемы-препроцессора:
имя-заголовка (только для директивы #include)
идентификатор (без различения ключевого слова)
константа
строковый-литерал
операция
пунктуатор
любой не-пробельный символ, не относящийся к предыдущим пунктам
имя-заголовка:
последовательность-символов-заголовка:
символ-заголовка
последовательность-символов-заголовка символ-заголовка
символ-заголовка:
любой символ из исходного множества символов, за исключением символа новой-строки (\n) или символа "больше чем" (>).
новая-строка:
символ новой строки
Пустая директива #
Пустая директива состоитиз строки, вкоторой содержится единственныйсимвол #. Эта директива всегда игнорируется препроцессором.
Директивы #define и #undef
Директива #define определяет макрос. Макросы обеспечивают механизм замены лексемы набором формальных, подобных используемых в функциях параметров, либо пустой замены.
Простые макросы #define
В простых случаях, без параметров, синтаксис данной директивы следующий:
#define идентификатор_макроса
Каждое вхождение идентификатора_макроса в исходный код после данной управляющей строки будет заменено на месте - возможно, пустой, -последовательностью_лексем (имеются некоторые рассматриваемые ниже исключения). Такие замены называются макрорасширениями. Последовательность лексем иногда называют телом макроса.
Любые вхожденияидентификаторамакроса, обнаруженное в литеральных строках, символьных константах или комментариях расширению не подлежат.
Пустая последовательность лексем позволяетэффективное удаление всех найденных идентификаторов макросов из исходного кода:
#define HI "Добрый день!"
#define empty
#define NIL ""
...
puts(HI); /* расширяется в: puts("Добрый день!"); */
puts(NIL); /* расширяется в: puts(""); */
puts("empty"); /* расширения empty не происходит ! */
/* расширение empty не будет выполнено и в комментариях! */
После расширения каждого конкретного макроса дальнейшее сканирование продолжится уже для нового, расширенного текста. Это дает возможность организации вложенных макросов: расширенный текст может в свою очередь содержать подлежащие расширению идентификаторы макросов. Однако, если макрос при расширении образует директиву препроцессора, то такая директива препроцессором уже не распознается:
#define GETSTD #include
...
GETSTD /* ошибка компиляции */
GETSTD будет расширен в #include. Однако, препроцессор не станет сам обрабатывать эту вполне допустимую в других условиях директиву, а передаст ее в таком виде компилятору. Компилятор воспримет#include как недопустимый ввод. Макрос не может быть расширен во время собственногорасширения. Поэтому выражения типа #define A A недопустимы вследствие неопределенности результата.
Директива #undef
Можно отменить определение макроса при помощи директивы #undef:
#undef идентификатор_макроса