Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 46
Текст из файла (страница 46)
Также нельзя использовать в них рекурсивные вызовы (препроцессор с ними не справится): №йефпе РК2(УТ (а, Ь) соиг«(а) «(Ь) №йефпе РЫХТ(а, Ь, с) сот«(а) « (Ь) « (с) /*бедах: нет перегрузки, переопределение" у №йелгпе ГАС(п) (п>1) тп*РАС(п-1):1 У*беда; рекурсивное макро*У Макросы связаны лишь с текстовой обработкой и они мало что знают о синтаксисе языка С++, и вообще ничего не знают о его типах и областях видимости.
Компилятор видит программный текст после макроподстановки, так что ошибки в макросах обнаруживаются лишь как последствия, а вовсе не в определениях макросов. Все зто приводит к весьма туманным сообщениям об ошибках. Вот макросы, внушающие доверие; №йе~япе САЯЕ Ьгеа№п сазе №йе(те РОКЕ)гЕК №ог (;; ) А вот примеры совершенно ненужных макросов: №йеЯпе Р1 3.141593 №йе)те ВЕС1с) ( №йеЯпе ЕМЭ ) Следующие макросы опасны: №йе3)пе Ям№УАКЕ(а) а*а №йе3)пе ИСК хх (хх) ьь Чтобы убедиться в их опасности, попробуйте применить их: УУ глобальный счетчик (пг хх = О; го(й 1'( ) ( №пгхх = О; (пгу = ЯДГУАКЕ(ххе2); И'СК хх; ) УУлокальная переменнак УУу=ххь2*ххь2( то есть у=ххьГ2*хх) ь2 УУ инкрементирует локальную хх Если уж вам действительно нужны макросы, применяйте операцию разрешения области видимости:: при ссылке на глобальные имена (В4.9.4) и везде, где только можно, заключайте в круглые скобки имена их аргументов.
Например: 215 7.8, Макросы ()зГеГшеМТ)у(а,Ь) (( (а) <(Ь) ) 7 (а): (Ь) ) Если вы написали довольно сложный макрос, так что требуются комментарии к нему, применяйте комментарии вида l* *l, ибо часто в состав инструментов программирования на С++ входит препроцессор языка С, а он ничего не знает о комментариях вида /!. Например: Импе М2 (а) поте(моя(а) ! * полезный комззентарий */ При помощи макросов вы можете создать свой собственный язык. Даже если вы сами предпочтете такой «улучшенный» язык простому С++, другим программистам он будет непонятен. Более того, препроцессор С вЂ” это очень простой макропроцессор.
Поэтому, когда вы захотите создать что-нибудь нетривиальное, то окажется, что либо это невозможно, либо неоправданно трудоемко. Механизмы сопзг, зпйпе, гешр2аге, епиш и пашезрасе являются альтернативой традиционному использованию препроцессорных конструкций. Например: еопзз (пг апзнег = 42; гетрйзге<е(аш Т> тяпе Ттт(Та, ТЬ) (ге(игп (а<Ь) эа:Ь() При помощи макросов можно создавать новые имена — новую строку можно составить из двух строк при помощи операции препроцессора ()().
Например: ()з(еуше Ь7АМЕ2(а, Ь) а()()Ь зпг Ь7АМЕ2 (Ьаей, саЬ) () з превратится в зпг Ьасйсаь () что и достанется компилятору для чтения. Директива препроцессора () ит(еу'Х гарантирует, что более не существует макроса с именем Х независимо от того, существовал ли он до этой директивы, или нет. Такой прием позволяет на всякий случай защититься от нежелательных макросов, так как в точности узнать их действие на фрагмент кода бывает нелегко.
7.8.1. Условная компиляция Одного случая применения макросов избежать практически невозможно. Директива компилятора Ьзуг2е2 н2епг5ег заставляет опустить последующую часть кода, пока не встретится директива ()епйг' .Например: шз Т(зпз а ()зу)(еу'агд ого ,зпгЬ ()епйТ )з превратится для компилятора в (пг Г'(зпг а Глава 7.
Функции 216 если не определен макрос агя пю. Данный код способен запутать автоматические инструменты разработки, так как они рассчитывают на разумное поведение программиста. Но в большинстве случаев характер применения №4НеУ менее эксцентричный, чем в рассмотренном примере, и при наличии определенных ограничений эта директива приносит мало вреда. См. также 89.3.3. Следует аккуратно выбирать имена макросов, используемых в директиве №гЩеу; чтобы они не вступали в противоречие с обычными идентификаторами. Например: змисг Сад !луо Юояе* агя оле; Холе* агд гив; ~7... )' Этот невинно выглядяший код вызовет недоразумения, как только кто-нибудь определит следуюший макрос: №йеЯпе ага око х К сожалению, многие стандартные заголовочные файлы содержат множество ненужных и опасных макросов.
7.9. Советы 1. Относитесь с подозрением к некоясгяаилгиым аргументам, передаваемым по ссылке; если вы хотите, чтобы функция модифицировала аргументы, используйте указатели и возврашаемое значение; 85.5. 2. Применяйте колстаятиьге ссылки, если вы хотите минимизировать копирование аргументов; 85.5. 3. Используйте совзг активно и последовательно; 87.2. 4. Избегайте макросов; 87.8. 5. Избегайте функций с неуказанным числом аргументов; 87.6. б. Не возвращайте указатели или ссылки на локальные переменные; 87.3.
7. Применяйте перегрузку функций в случаях, когда концептуально одинаковая работа выполняется над данными разных типов; 87.4. 8. В случае перегрузки функций с целыми аргументами реализуйте полный набор вариантов для устранения неоднозначностей; 87.4.3. 9. Обдумывая вопрос о применении указателей на функций, рассмотрите возможность их замены на виртуальные функции 52.5.5) или шаблоны (52.7.2) в качестве лучших альтернатив; 87.7. 10. Если вам нужны макросы, применяйте для них безобразно выглядящие имена из заглавных букв; 87.8. 217 7.10.
Упражнения 7.10. УПРажНЕНИЯ 1. 2. ~уредеТ«пГ (аг(уй) (ГпГ, (иГ); «мисг Тподе «гное и огд; (пг соилы Тподе" !«71; Тподе* ггдЛГ )' 3. 4. 5. 6. (*1) Напишите следующие объявления: функция с аргументами «указатель на символ» и «ссылка на целое», не имеющая возврата; указатель на такую функцию; функция с таким указателем в качестве аргумента; функция, возвращающая такой указатель. Напишите определение функции, принимающей такой указатель в качестве аргумента и возвращающей его же. Подсказка; воспользуйтесыуредеТ. (*1) Что означает следующее? Для чего это может потребоваться? ('1.5) Напишите программу типа «Не!!о, т«ог!сЬ>, которая берет имя (пагпе) из командной строки и выводит «Не1!о, пате.'».
Модифицируйте программу так, чтобы она могла брать произвольное количество имен в качестве аргументов и приветствовать всех по этим именам. (*1.5) Напишите программу, которая читает произвольное количество файлов, чьи имена задаются в командной строке, и выводит их последовательно в соиг.
Поскольку эта программа соединяет содержимое своих аргументов для формирования вывода, можете назвать ее саг. (*2) Преобразуйте небольшую программу на языке С в соответствуюшую программу на С++. Переделайте заголовочные файлы таким образом, чтобы в них объявлялись все вызываемые в программе функции, и чтобы были указаны типы всех их аргументов. Где можно, замените все директивы 1де7?ие на еиииг, сола или )и11ие.
Удалите все объявления ехгегп из . с-файлов и в случае необходимости преобразуйте определения функций в синтаксисе языка С в соответствующие определения в синтаксисе языка С++. Замените вызовы функций гпа11ос() иерее!) на операции пепи де!его, Удалите явные преобразования типов, в которых нет необходимости. (*2) Реализуйте функцию хгогг() 57.7) с помощью более эффективного алгоритма сортировки.
Подсказка: деогг() . (*2.5) Имеется структура Люде: Напишите функцию для внедрения новых слов в дерево с узлами типа Тиоде. Напишите функцию для вывода такого дерева. Напишите функцию для вывода слов из такого дерева в алфавитном порядке. Модифицируйте структуру Тподе так, чтобы она содержала указатель на произвольно длинное слово, хранящееся в массиве символов, память под который вьщеляется операцией пет«. Модифицируйте все функции, чтобы они работали с новым вариантом структуры Тподе. 218 Глава 7.
Функции 8. (*2.5) Напишите функцию, инвертируюшую (транспонирующую) двумерный массив. Подсказка: 8С.7. 9. (*2) Напишите программу шифрования, которая читает из свд и пишет зако- дированные символы в соиг. Можете применить следующую простую схему шифрования: символ с кодируется выражением с"7(еу(11, где йеу — строка, передаваемая в качестве аргумента командной строки.
Программа циклически использует символы из строки йеу до исчерпания ввода. Повторное шифрование по этой же формуле восстанавливает исходный текст. Если нет входного текста или йеу есть нулевая строка, шифрование не выполняется. 10. (*3.5) Напишите программу, помогающую без знания ключа дешифровать текст, закодированный программой из предыдущего упражнения. Подсказка: 1)атЫ Кайн: 7))е СодеЬгеаlсегз, Маспп1!ап, 1967, )к)ев Уог)с, рр. 207-213. 11. (*3) Напишите функцию егеог(), принимающую строку в стиле функции рг!л1У(), содержащую Ьз, Ьс и Ь4, и произвольное число других аргументов. Не используйте функцию рпагу() . См.
821.8. Используйте <глтг7аед>. 12. (*1) Как бы вы выбирали имена для типов указателей на функции, которые определяются с помощью )уреаеу? 13. (*2) Просмотрите несколько программ, обращая внимание на множество стилей именования. Как используются заглавные буквы? Как используется символ подчеркивания? Когда используются короткие имена вроде !или х? 14. (*1) Что плохого в следующих макросах? Иерее Р1 = 3. 141593; влейте МАХ(а, Ь) а>Ь?а:Ь Ие))пе)ас(а) (а) "/ас( (а) -1) 15. (*3) Напишите простой макропроцессор, позволяющий определять и расши- рять макросы (как это делает препроцессор языка С). Читайте из с)л и выводите в соиг.
Вначале ограничьтесь макросами без аргументов. Подсказка: программа-калькулятор (86.1) содержит таблицу символов и лексический анализатор, которыми вы можете воспользоваться. 16. (*2) Реализуйте функцию реЬлг() из 87.5. 17. (*2) Добавьте функции, такие как з9еГ(), 7оа() и е(л(), к программе-кальку лятору из 86.1. Подсказка: заранее определите эти имена и вызывайте функции с помощью массива указателей на функции. Не забывайте проверять фактические аргументы вызова. 18. (*1) Напишите функцию вычисления факториала, не использующую рекур сию.
См. 811.14(6]. 19. (*2) Напишите функции, которые добавляют день, месяц, год к заданной дате (структура Ваге из 85.9(! 3]). Напишите функцию, вычисляющую день недели для заданного значения Ваге. Напишите функцию, вычисляющую значение Эаге для первого понедельника после заданного Раге. Пространства имен и исключения Год 787! От Розкдества Христова? — Монти Пайтон Иет столь общих правил, что не допускали бы исключении. — Роберт Бартон Модульность — интерфейсы и исключения — пространства имен — ив1пя — ив1пя натеврасе — разрешение конфликтов имен — поиск имен — композиция пространств имен — псевдонимы пространств имен — пространства имен и код на С вЂ” исключения — гйгою и сигей — исключения и структура программы — советы — упражнения.
8.1. Разбиение на модупи и интерфейсы. Любая реальная программа состоит нз нескольких отдельных частей. Например, даже столь простая программа, как «Не11о, чгог1д!», включает по крайней мере две части: пользовательский код, требующий выполнить вывод строки Не11о, ног1д!, и система ввода-вывода, которая н осуществляет этот вывод. Снова рассмотрим программу-калькулятор из В6.1. Ее можно рассматривать как совокупность пяти частей: 1. Парсера, выполняющего синтаксический анализ 2.
Лексического анализатора, составляющего лексемы из символов 3. Таблицы символов, содержащей пары (строка, значение) 4. Управляющей части, содержащей функцию та1п() 5. Обработчика ошибок Глава 8. Пространства имен и исключения Это можно отобразить графически: Здесь стрелками обозначено отношение «использует», Чтобы упростить рисунок, я не стал на нем отображать тот факт, что все части используют обработчик ошибок. По сути, калькулятор состоит лишь из трех частей, а управляюшая часть («драйвер») и обработчик ошибок введены в него для полноты реализации. Когда один модуль использует другой модуль, ему нет никакой необходимости знать все детали устройства последнего.