|
Программирование >> Унарные и бинарные операторы
1 04 Глава 5. Функции, указатели и ссылки Указатель на функцию 105 Чтобы убедиться в том, что программа работает правильно, можно записать в какую-нибудь ячейку массива отрицательное число и посмотреть, что будет выведено на экран. Указатель на функцию Слова указатель на функцию звучат дико, мы ведь привыкли к тому, что указывать можно только на объекты, такие как числа, строки и массивы. Но ведь и функция, если подумать, - тоже объект, правда, состоящий не из букв и чисел, а из команд процессора. Как и любые другие объекты, функция занимает определенный участок компьютерной памяти, и, когда она вызывается, процессор пересылает аргументы в особое, известное каждой функции место памяти, а затем просто получает адрес ее первой инструкции. Вот этот адрес и есть указатель на функцию. Теперь подумаем, как объявить этот указатель. Очевидно, больше всего функция похожа на массив, поэтому и указатель на нее выбран таким: int (*f)Cint.int): Здесь f - указатель на функцию, принимающую два целочисленных параметра и возвращающую целое число. Указатель на функцию, как и любой другой, инициализируется оператором &. В программе из листинга 5.10 объявлена функция sum, принимающая две целочисленные переменные и возвращающая их сумму. Кроме того, объявлен указатель Г на аналогичную функцию. Как только f получает адрес начала sum, открывается партизанская тропа к этой функции. Оператор * превращает, как обычно, ука.затель на функцию в саму функцию, следовательно, (*f) (2.3) вернет пятерку, потому что *f - это как бы второе имя sum. Заметим, что скобки вокруг *f нужны, ведь int *f С int. int) - это совсем не указатель на функцию, а функция, возвращающая указатель. Листинг 5.10 #inc1ude <iostream> using namespace std; 1nt sum(int a. int b){ return(a+b); int main{){ int {*f)(int.int): cout sum{2.3) endl: f=&sum: cout (*f){2.3) endl; return 0; Обращение к функции через указатель {*f)(2.3) весьма похоже на обра1цение к переменным, но давайте вспомним о сходстве функций и массивов. Если m - массив переменных типа char, а pch - указатель на char, то присвоить указателю адрес нулевого элемента массива можно двумя способами: pch=&m[0] и просто pch=m. То есть имя массива - это еще и адрес его начала. Проделать такое можно потому, что с именем массива не связано значение какого-то его элемента. Если ср - переменная трша char, а pch - указатель па char, то нельзя записать pch=ch, потому что слева и справа от знака равенства стоят объекты разной породы . Но если m - имя массива, состоящего из переменных char, наши руки развязаны, и можно записать pch=m, ничего не нарушая. То, что справедливо для массивов, должно выполняться и для функций. Ведь имя функции так же свободно , как имя массива. Поэтому имя функции - и есть адрес ее начала, а это значит, что инициализация указателя на функцию не требует оператора &. Очевидно, ана-логию с массивами нужно довести до логического конца. Если адрес нулевого элемента массива используется наравне с его именем, то такой же привилегией должен обладать и ука.затель па функцию. И.мснно это и подтверждает листинг 5.11, где указатель на функцию и се на-стояп1ее имя оказываются равноправными. Листинг 5.11 include <iostream> using namespace std; int sumCint a, int b){ return(a+b); int main{){ int C*f)Cint.int): cout sum{2.3) endl: f=sum: cout f(2.3) endl: return 0; Нужно, правда, помнить, что имя функции - не указатель, поскольку не может быть перенаправлено на другую функцию. Настоящий указатель свободен и волен указывать куда угодно, в том числе и в никуда. Чтобы у читателя не сложилось впечатление об указателе на функцию, как о чем-то любопытном, по бесполезном, попробуем !юписать про1рамму, реализуюп1ую разные способы сортировки строк. До сих пор для этого использовалась функция sortO, принимаюи1ая только два отератора - s.beginO и s.endO, указывающие на начало и конец контейнера (см. раздел Файлы в главе 4). При этом строки размс1цалнсь в лекснкоп)афическом порядке, грубо говоря, но алфавиту. Но сун1ествует множество других способов сравнения строк. Например, большей можно считать ту строку, что длиннее, поэтому существует другая функция sortO - уже с тремя параметрами. Первые два - уже знакомые нам итераторы, а третий - указатель на ()ункцию, выполняюн1ую сравнение строк. Эта функция принимает два сравниваемых объекта (в наиюм случае они имеют тип string) и возвращает булеву пе1эеменную - результат сравнения. Еслп Эта программа сначала создает контейнерный тин vector, содержащий шесть объектов типа string, а затем сортирует его двумя разными способами, подставляя в функцию sort() указа Tejni на разные функции. В первом случае (функция спр) оператор < сравнивает строки лексикографически. Во втором (функция cmpl) сравниваются размеры строк, для чего используются собственные функции sizeO объектов string. Естественно, объекты сортируются по нозрасганию , то функция должна возвратить true, когда первый обтлкт больше второго, и false - в протпв1Юм случае (листинг5.12). Листинг 5.12 #include <iostream> #include <vector> #include <algorithn> using namespace std: bool cmpcstring xl. string x2) { return xl < x2: bool cmpl(string xl. string x2) { return xl.sizeO < x2.s1ze(): } int main(){ vector<string> s: s.pushbackCqapb ): s.push back( царевич ); s.push back( король ): 5.ри5Ь Ьаск( королевич ): s.pusl back( caпoжник ); s.push back( портной ): sort(s.begin().s.end().cmp): for(int i=0:is.size():1++) cout s[i] endl: cout endl: sort(s.begi nC).s.end().cmpl): for(int i=0:i<s.size():i++) cout s[i] endl: return 0: Ссылки Так сдружились они, Браконьер и Бобер (Свет не видел примера такого!). Что никто и нигде никогда с этих пор Одного ие встречал без другого. Льюис Кэрролл, *Охота на Снарка На первый взгляд ссылки ничем не напоминают указатели. Гораздо больше они похожи на псевдонимы переменных, которые можно испол1>зовать наравне с подлинными именами. Ссылка объявляется с помощью оператора &, например: int &b = а: Здесь а - исходная целочисленная переменная, b - ссылка на нее. В отличие от указателя, ссылка не может существовать сама по себе. Только что объявленная ссылка намертво связывается с переменной, и разлучить их невозможно. Программа, показанная в листинге 5.13, сначала объявляет переменную а и присваивает ей начальное значение - двойку. Затем объявляется b - ссылка на переменную а. Делается это с помощью оператора &, причем его положение между i nt и b не имеет значения. Следующие объявления эквивалентны: int&b = а; int& b = а: int &b = а; Листинг 5.13 #inc1ude <iostream> using namespace std: int main(){ int a=2; int &b=a: cout b= b endl: b=2 b-5: cout a= a endl: a=5 return 0: В нашей программе ссылке не присваивается никакого начального значения, да в этом и нет нужды, потому что переменная b - это та же переменная а, но иначе называемая. Поэтому сразу после объявления переменная b равна двум. Но если присвоить b иное значение, например 5, то и переменная а гакжс станет равной пяти. Связь ссылки и самой переменпой можно использовать при передаче аргументов функции. Если персда-ет!:;я ссылка, то ее изменение должно отражаться на самой переменной. Правда, для этого нужно явно ука.зать функции, что она принимает именно ссылку. Программа, показанная в листинге 5.14, делает то же, что программа из листинга 5.5, - меняет содержимое переменных а и Ь. Но на этот раз функция exchngC) принимает не указатели, а ссылки на переменные. Листинг 5.14 #inc1ude <iostream> using namespace std: void exchngCint Sa. int &b){ int tmp=a: a=b: b=tmp: int main(){ int x=2.y=5: exchng(x.y): cout x= X endl: cout y- у end!: return 0: } О tom, что функция получает ссылки, а не указатели, говорит заголовок функции void exchngC i nt &a. int &b), результаты сортировки оказываются разными, и будет поучительно па них посмотреть.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.503
При копировании материалов приветствуются ссылки. |