С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 60
Текст из файла (страница 60)
Хотя элементы разделены, elem все еще остается первым вмассиве. swap() в строке 19 ставит его на место до рекурсивного вызова sort() для двухчастей массива.Сравнение производится вызовом функции, на которую указывает compare (строки 1112). Чтобы поменять элементы массива местами, используется операция swap() саргументами типа string, представленная в разделе 6.11.#include <iostream>#include <string>// это должно бы находиться в заголовочном файлеint lexicoCompare( const string &, const string & );int sizeCompare( const string &, const string & );typedef int (*PFI)( const string &, const string & );void sort( string *, string *, PFI=lexicoCompare );string as[10] = { "a", "light", "drizzle", "was", "falling","when", "they", "left", "the", "museum" };int main() {// вызов sort() с значением по умолчанию параметра comparesort( as, as + sizeof(as)/sizeof(as[0]) - 1 );// выводим результат сортировкиfor ( int i = 0; i < sizeof(as)/sizeof(as[0]); ++i )cout << as[ i ].c_str() << "\n\t";Вот как выглядит main(), в которой применяется наша функция сортировки:}Результат работы программы:"a""drizzle""falling""left""light""museum""the""they""was""when"Параметр функции автоматически приводится к типу указателя на функцию:366С++ для начинающих// typedef представляет собой тип функцииtypedef int functype( const string &, const string & );void sort( string *, string *, functype );void sort( string *, string *,sort() рассматривается компилятором как объявленная в видеint (*)( const string &, const string & ) );Два этих объявления sort() эквивалентны.Заметим, что, помимо использования в качестве параметра, указатель на функцию можетбыть еще и типом возвращаемого значения.
Например:int (*ff( int ))( int*, int );ff() объявляется как функция, имеющая один параметр типа int и возвращающаяуказатель на функцию типаint (*)( int*, int );И здесь использование директивы typedef делает объявление понятнее. Объявив PF с// Использование директивы typedef делает// объявления более понятнымиtypedef int (*PF)( int*, int );помощью typedef, мы видим, что ff() возвращает указатель на функцию:PF ff( int );Типом возвращаемого значения функции не может быть тип функции. В этом случае// typedef представляет собой тип функцииtypedef int func( int*, int );выдается ошибка компиляции.
Например, нельзя объявить ff() таким образом:func ff( int ); // ошибка: тип возврата ff() - функция7.9.6. Указатели на функции, объявленные как extern "C"Можно объявлять указатели на функции, написанные на других языкахпрограммирования. Это делается с помощью директивы связывания. Например,указатель pf ссылается на С-функцию:extern "C" void (*pf)(int);367С++ для начинающихextern "C" void exit(int);// pf ссылается на C-функцию exit()extern "C" void (*pf)(int) = exit;int main() {// ...// вызов С-функции, а именно exit()(*pf)(99);Через pf вызывается функция, написанная на языке С.}Вспомним, что присваивание и инициализация указателя на функцию возможны лишьтогда, когда тип в левой части оператора присваивания в точности соответствует типу вправой его части.
Следовательно, указатель на С-функцию не может адресовать функциюС++ (и инициализация его таким адресом не допускается), и наоборот. Подобная попыткаvoid (*pfl)(int);extern "C" void (*pf2)(int);int main() {pfl = pf2; // ошибка: pfl и pf2 имеют разные типы// ...вызывает ошибку компиляции:}Отметим, что в некоторых реализациях С++ характеристики указателей на функции С иС++ одинаковы.
Отдельные компиляторы могут допустить подобное присваивание,рассматривая это как расширение языка.Если директива связывания применяется к объявлению, она затрагивает все функции,участвующие в данном объявлении.В следующем примере параметр pfParm также служит указателем на С-функцию.Директива связывания применяется к объявлению функции, к которой этот параметр// pfParm - указатель на С-функциюотносится:extern "C" void f1( void(*pfParm)(int) );Следовательно, f1() является С-функцией с одним параметром – указателем на Сфункцию.
Значит, передаваемый ей аргумент должен быть либо такой же функцией, либоуказателем на нее, поскольку считается, что указатели на функции, написанные наразных языках, имеют разные типы. (Снова заметим, что в тех реализациях С++, гдеуказатели на функции С и С++ имеют одинаковые характеристики, компилятор можетподдерживать расширение языка, позволяющее не различать эти два типа указателей.)Коль скоро директива связывания относится ко всем функциям в объявлении, то как жеобъявить функцию С++, имеющую в качестве параметра указатель на С-функцию? Спомощью директивы typedef.
Например:368С++ для начинающих// FC представляет собой тип:// С-функция с параметром типа int, не возвращающая никакого значенияextern "C" typedef void FC( int );// f2() - C++ функция с параметром // указателем на С-функциюvoid f2( FC *pfParm );Упражнение 7.21В разделе 7.5 приводится определение функции factorial(). Напишите объявлениеуказателя на нее. Вызовите функцию через этот указатель для вычисления факториала 11.Упражнение 7.22(a) int (*mpf)(vector<int>&);(b) void (*apf[20])(doub1e);Каковы типы следующих объявлений:(c) void (*(*papf)[2])(int);Как сделать эти объявления более понятными, используя директивы typedef?Упражнение 7.23double abs(double);double sin(double);double cos(double);Вот функции из библиотеки С, определенные в заголовочном файле <cmath>:double sqrt(double);Как бы вы объявили массив указателей на С-функции и инициализировали его этимичетырьмя функциями? Напишите main(), которая вызывает sqrt() с аргументом 97.9через элемент массива.Упражнение 7.24Вернемся к примеру sort().
Напишите определение функцииint sizeCompare( const string &, const string & );Если передаваемые в качестве параметров строки имеют одинаковую длину, тоsizeCompare() возвращает 0; если первая строка короче второй, то отрицательное число,а если длиннее, то положительное. Напоминаем, что длина строки возвращаетсяоперацией size() класса string. Измените main() для вызова sort(), передав вкачестве третьего аргумента указатель на sizeCompare().369С++ для начинающих8.
Область видимости и время жизниВ этой главе обсуждаются два важных вопроса, касающиеся объявлений в С++. Гдеупотребляется объявленное имя? Когда можно безопасно использовать объект иливызывать функцию, т.е. каково время жизни сущности в программе? Для ответа напервый вопрос мы введем понятие областей видимости и покажем, как ониограничивают применение имен в исходном файле программы. Мы рассмотримразные типы таких областей: глобальную и локальную, а также более сложноепонятие областей видимости пространств имен, которое появится в конце главы.Отвечая на второй вопрос, мы опишем, как объявления вводят глобальные объектыи функции (сущности, “живущие” в течение всего времени работы программы),локальные (“живущие” на определенном отрезке выполнения) и динамическиразмещаемые объекты (временем жизни которых управляет программист). Мытакже исследуем свойства времени выполнения, характерные для этих объектов ифункций.8.1. Область видимостиКаждое имя в С++ программе должно относиться к уникальной сущности (объекту,функции, типу или шаблону).
Это не значит, что оно встречается только один раз во всейпрограмме: его можно повторно использовать для обозначения другой сущности, еслитолько есть некоторый контекст, помогающий различить разные значения одного и тогоже имени. Контекстом, служащим для такого различения, служит область видимости. ВС++ поддерживается три их типа: локальная область видимости, область видимостипространства имен и область видимости класса.Локальная область – это часть исходного текста программы, содержащаяся вопределении функции (или в блоке). Любая функция имеет собственную такую часть, икаждая составная инструкция (или блок) внутри функции также представляет собойотдельную локальную область.Область видимости пространства имен – часть исходного текста программы, несодержащаяся внутри объявления или определения функции или определения класса.Самая внешняя часть называется глобальной областью видимости или глобальнойобластью видимости пространства имен.Объекты, функции, типы и шаблоны могут быть определены в глобальной областивидимости.
Программисту разрешено задать пользовательские пространства имен,заключенные внутри глобальной области с помощью определения пространства имен.Каждое такое пространство является отдельной областью видимости. Пользовательскоепространство, как и глобальное, может содержать объявления и определения объектов,функций, типов и шаблонов, а также вложенные пользовательские пространства имен.(Они рассматриваются в разделах 8.5 и 8.6.)Каждое определение класса представляет собой отдельную область видимости класса.(О таких областях мы расскажем в главе 13.)Имя может обозначать различные сущности в зависимости от области видимости.