Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 45
Текст из файла (страница 45)
Это иллюстрирует те дополнительные тонкости и сложности, с которыми приходится иметь дело программисту, как только из-за применения многоточия пропадает проверка типов компилятором. Определим функцию вывода сообщений об ошибках следующим образом: юЫ епог (1нз зегег/(у... ) // За "зеиеп(у" следует список элементов //типа сьаг* с терминальным нулем ( га 1вцар; га згаы (ар, зевес((у); 1ог(;; ) ( сваг" р = га ага(ар, слег*) (7 (р == 0) ьгеаь/ сегг«р« ' га ела(ар) сегг <с ' 'чп'; Ц'(зегег(зу) ехй (зегег((у) ) Сначала создается переменная типа га 11зг и инициализируется вызовом га згагг О .
Макрос га згагг в качестве аргументов берет имя указанной переменной и имя последнего формального параметра нашей функции епог(). Макрос га агя() последовательно извлекает неименованные фактические аргументы. При каждом вызове программист должен указывать ожидаемый тип; макрос га азж() полагает, что тип переданного аргумента в точности соответствует указанному программистом, но не может это проверить. Перед выходом из функции, использовавшей еа згагг(), нужно вызвать еа еаИ(), так как га згагг() может модифицировать стек таким образом, что нормальный выход из функции становится невозможным. Вызов га еадО устраняет эти модификации.
7.7. Указатели на функции юЫ епог(ззг/па з) ( /* ... */ ) гоЫ(*вас!) (эппл) ( //указатель на функцию иоЫ/'() еусз= ь егюг; е/сз ("еггог" ); ) // е/с( указывает на функцию еггог // вызов еп ог через е/с( Компилятор легко обнаруживает, что е/ст является указателем на функцию, и вызывает функцию по адресу, содержащемуся в этом указателе.
При этом разыменовоние С функциями можно выполнить лишь два вида работ: вызвать или вычислить их адрес. Указатель, которому присвоен адрес функции, можно далее использовать для вызова этой функции. Например: Глава 7. Функции указателя на функ((ию (то есть применение операции *) не обязательно. Также не обя- зательно использовать явным образом операцию в для получения адреса функции: гоЫ("17) (зсг!ли) = ьеггог; //о)с воЫ( "12) (зсг!пд) = еггог; //о/с тот же смысл, что и дсеггог // о!с //тоже о(с Для указателей на функции надо объявлять типы аргументов так же, как и дяя самой функции. Присваивание указателей на функции друг другу допустимо лишь при полном совпадении типов функций.
Например: У о!с //ег огч не тот возвращаемый тип // еггогс не тот тип аргумента Р1(тНега") / рТ(1) ! // о(с //еггогс не тот тип аргумента !п! с'=р ( "леиз" ) ) Правила передачи аргументов при вызове функций через указатели те же самые, что и при непосредственном вызове функций. Часто бывает удобным один раз определить синонимичное имя для типов указателей на функции, чтобы избежать многократного использования достаточно неочевидного синтаксиса их объявления.
Вот пример из заголовочного файла системы (Лч]]Х: сурес!еГкоЫ (с$1С Тур) ((п!);,уиз <з!епа!)с) суресгеу гоЫ (*$1С АЯС ТТР) (сл! ) ! $1С ТТР ту!ла!(Ыс, $1С А/7С ТТР) ! Часто бывают полезными массивы указателей на функции. Например, система меню в моем графическом редакторе, реализована через массивы указателей на функции, каждая из которых выполняет соответствующее действие. В целом это решение нельзя здесь рассмотреть в деталях, но вот его основная идея; суресге~'воЫ (*РГ) (); // команды редактированин: РГ ес((! орз(] =( асис, араме, ьсору, ьзеагс!с] // файловые операции: РГЯе орз,"] =( верен, варрелсг, ьс!озе, ьвмсе); воЫд() ( 77(-Г ('77) (" Магу 7(озе" ); гоЫ (*рф (зсг!па) ! гоЫ77 (зсг(пе) ! (л! 72 (зсг(пе); юЫ13 (!лс*) ! го(с(7' ( ) ( 77"= й!)с7! р!'= в!72! РТ= в177! //указатель на гоЫГзсмпя! // чо(дГзсг(пф // (пс/зсг(пд) // го(дГ(псе! //егюгс юЫ присваивается переменной типа ии 7.7.
Указатели на функции Теперь можно определять и инициализировать указатели, ответственные за выполнение действий, инициируемых при помощи меню и кнопок мыши: РГ* Ьилол2 = еди орз; РГ* ЬипопЗ = 2)!е орз; В полной реализации с каждым пунктом меню нужно связывать больше информации. Например, нужно где-то хранить строку с текстом данного пункта меню. Во время работы роль кнопок мыши может изменяться в зависимости от контекста.
Такие изменения реализуются (частично) изменением значений указателей, относящихся к кно)ткам. При выборе конкретной кнопкой (например, кнопкой 2) некоторого пункта меню (например, пункта 3) выполняется соответствующая операция: // вызов 3-ей функции из Ьииоп2 Ьипол2(2] (); Чтобы по достоинству оценить всю мощь указателей на функции, нужно попробовать написать подобный код без их помощи, и без помощи их еще более полезных родственников — виртуальных функций 5! 2.2.6). Меню можно модифицировать во время выполнения программы, просто добавляя новые функции (обработчики) в таблицу операций данного меню.
Также легко создавать новые меню динамически (во время выполнения программы). Указатели на функции можно применить для реализации простой формы полиморфных процедур, то есть процедур, применимыл к обьектам розных типов: еуреде/ 1л!(*СРТ) (соле! воЫ*, соил! го!д*) еоЫ ззогз(еоЫ* Ьазе, з(се ! л, з(зе ззз, СгТстр) /* Сортировка л элементов вектора базе в возрастающем порядке с использованием функции сравнения, указуемой с помощью стр. Элементы имеют размер зз.
5Ье!! зоп (Кпи(Ь, )го!3, рд84) */ ( /ог (!пз бар=и/2; 0<дар; бар/=2) /ог(1пг !=бар/ 1<п; 1+ь) /ог(1пг)=1-дарг 0<=/; 1-=бар) ( сяаг* Ь=табс саз!<сааг*> (Ьазе) ! //обязательное приведение типа сйаг* р!=Ь+1*зс! // Абазе!(/ сваг* р(0-Ьь ((ьяар) *зг Д йЬазе(/+Кар) // обменять Ьазе(/) и базе(/лиар); (((стр (р)0, р!) <О) ( 3ог(т! Й=О; Ь<зт/ А++) ( слог гетр =р!(Ь); р)(Ь) =р)а(Ь): р/0(Ь) = гетр; ейе Глава 7. Функции Ьгеаа; (7зег Ьеайз [1 = ( "В!!сЫе В .
М", "Яе!Ы М. ", "ЯгутапзЫ Т. С. ", "ЯсЬ«уе«Ю. Е . ", "Юсл«уе«Х. А . ", "Кеги(алаи В. )«'. ", иоЫ р«!л! Ы (Сзег* и, 1л! и) ( !ог(1и! 1=0( 1<и( 1е-~) сои!« « [11 .лате« ' )!'« «[1) .Ы« ) 11271, 11272, П275, П274, 11275, 11276 ); '!' ««[1) .Иер!« ' ~и'; Нужно еще определить подходящую функцию сравнения. Такая функция долж- на возвращать отрицательное значение, когда первый аргумент меньше второго, нуль в случае равенства аргументов, и положительное число в остальных случаях: т! стр1 (соим юЫ* р, сопи иоЫ* а) 77 сравнение имен ( ге«игл зтстр ( Маис сов!<соле! !7зег*> (р) ->лате, з!аис сиз!<сот! 1!ее«*> (а) ->лате ); ) (и!стр2(соим иоЫ* р, сопи юЫ* о) «с<и«п зга1)с сов!<сот! 1!ее«*> (р) ->з(ер!— з!а(1с саз!<соле! 17зег*> (о) ->Мер!; 77 сравнение номеров отделов Саму сортировку и вывод ее результатов выполним в функции тай(): 1л! та)и () ( сот« "Неадз !п а!рааЬеиса1 огйею Хп"; Процедура ззогг() не знает типы объектов, которые она сортирует, а только число элементов (размер массива), размер каждого элемента н функцию, используемую для сравнения элементов.
Тип процедуры ззогг() намеренно выбран таким же, как у стандартной сортируюшей процедуры азогг() из библиотеки языка С. В реальных программах применяются азогт(), алгоритм зогз() стандартной библиотеки С++ (818.7.1), или специализированные процедуры сортировки. Рассмотренный стиль кодирования типичен для языка С, но его нельзя назвать наиболее подходящим способом формулирования алгоритмов на языке С++ (813.3, 813.5.2).
Разработанная нами процедура ззог!() применима для сортировки табличной информации: и. (и сЬаг* пате; ела«* 1И; Ьм Иер!1 7.8. Макросы зюг! (Ьеайз, б, з(гео((Иег), сгпр! ) ! рюи !й(Ьеайз, 6); сои!« ' ~и '; сои!« "Неайз !п огйег от йераггтеп! питбег: ~п"; зюгг(веайз, 6, з!гео((Нзег), стр2) ! ргшг Ы(Ьеайз,б); ) Указателям на функции можно присваивать адреса перегруженных функций. В таких случаях тип указателя используется для выбора нужного варианта перегруженной функции. Например: го!й Г"(!и!); !п! г(сваг); (г' го(й (((и() !! и! ~(сбаг! !! еггог; нет функции го1й ((сЬаг! юЫ (*рЯ) (!и!) =и(; !пг(*р1г.) (сваг) =й(! юЫ (*р(З) (сваг) =й(! Функции вызываются через указатели на функции исключительно с правильнымн типами аргументов и возвращаемых значений.
Когда производится присваивание указателям на функции или выполняется их инициализация, никаких неявных преобразований типов аргументов или типов возвращаемых значений не производится. Это означает, что функция !п! стрЗ (сопя! ту(уре*, сопя! тугуре*); не подходит в качестве аргумента для звог! () . Причина в том, что в противном слу- чае была бы нарушена гарантия вызова стрЗ() с аргументами типа сонм ту!уре* (см.
также 59.2.5). 7.8. Макросы бйе() пе МАМЕ гез! оу !ше Всюду, где встречается лексема 1(АМЕ, она заменяется на гезгц(1!пе. Например; пагпей = МАМЕ Макросы очень полезны в языке С, но в языке С++ они используются гораздо реже. Самое первое правило для макросов: не используйте их без крайней необходимости. Почти что каждый макрос свидетельствует о наличии слабых мест в языке, программе или программисте.
Так как из-за макросов текст программы изменяется до того, как его увидит компилятор, то создаются лишние проблемы для многих инструментов программирвания. Если вы применяете макросы, приготовьтесь получать меньшую пользу от отладчиков, генераторов перекрестных ссылок и профилировщиков. Если все же вам нужно применять макросы, внимательно прочитайте руководство по вашей реализации препроцессора С++ и не прибегайте к уж слишком хитрым конструкциям. Также следуйте общепринятому соглашению об именовании макросов с помощью исключительно заглавных букв.
Синтаксис макросов описан в 5А.11. Простейший макрос определяется следующим образом; Глава 7. Функции превратится в патей = гем оГ Кое Можно определять макросы с аргументами. Например: №йефпе МАС(х,у) агеитеп(1: х агдитеп(2: у В местах, где применяется макрос МАС, должны также присутствовать и два строковых аргумента. Они заменят х и у при макроподстановке. Например, ехрапйей = МАС ~оп Ьаг, уиа уиа) превратится в ехрапйей = агяитеп(1: Тоо Ьаг агяитепг2: уиа уиа Имена макросов перегружать нельзя.