Лекции.ИНФО


Использование long-функции myabs(): 9



При выполнении эта программа создает три похожие, но все же различные функции, вызываемые с использованием "общего" (одного на всех) имени myabs. Каждая из них возвращает абсолютное значение своего аргумента. Во всех ситуациях вызова компилятор "знает", какую именно функцию ему использовать. Для принятия решения ему достаточно "взглянуть" на тип аргумента, передаваемого функции. Принципиальная значимость перегрузки состоит в том, что она позволяет обращаться к связанным функциям посредством одного, общего для всех, имени. Следовательно, имя myabs представляет общее действие, которое выполняется во всех случаях. Компилятору остается правильно выбрать конкретную версию при конкретных обстоятельствах. Благодаря полиморфизму программисту нужно помнить не три различных имени, а только одно. Несмотря на простоту приведенного примера, он позволяет понять, насколько перегрузка способна упростить процесс программирования.

Каждая версия перегруженной функции может выполнять любые действия. Другими словами, не существует правила, которое бы обязывало программиста связывать перегруженные функции общими действиями. Однако с точки зрения стилистики перегрузка функций все-таки подразумевает определенное "родство" его версий. Таким образом, несмотря на то, что одно и то же имя можно использовать для перегрузки не связанных общими действиями функций, этого делать не стоит. Например, в принципе можно использовать имя sqr для создания функции, которая возвращает квадрат целого числа, и функции, которая возвращает значение квадратного корня из вещественного числа (типа double). Но, поскольку эти операции фундаментально различны, применение механизма перегрузки методов в этом случае сводит на нет его первоначальную цель. (Такой стиль программирования, наверное, подошел бы лишь для того, чтобы ввести в заблуждение конкурента.) На практике перегружать имеет смысл только тесно связанные операции.

Анахронизм в виде ключевого слова overload

На заре создания C++ перегруженные функции необходимо было явным образом объявлять таковыми с помощью ключевого слова overload. Это ключевое слово больше не требуется в C++. В действительности стандартом C++ оно даже не включено в список ключевых слов. Однако время от времени его можно встретить в каком-нибудь С++-коде, особенно в старых книгах и статьях.

Общая форма использования ключевого слова overload такова.

overload func_name;

Здесь элемент func_name представляет собой имя перегружаемой функции. Эта инструкция должна предшествовать объявлениям перегруженных функций. (В общем случае оно встречается в начале программы.) Например, если функция Counter() является перегруженной, то в программу могла быть включена такая строка.

overload Counter;

Если вы встретите overload-объявления при работе со старыми программами, их можно просто удалить: они больше не нужны. Поскольку ключевое слово overload — анахронизм, его не следует использовать в новых С++-программах. На самом деле большинство компиляторов его попросту не воспримет.

Аргументы, передаваемые функции по умолчанию

В C++ мы можем придать параметру некоторое значение, которое будет автоматически использовано, если при вызове функции не задается аргумент, соответствующий этому параметру. Аргументы, передаваемые функции по умолчанию, можно использовать, чтобы упростить обращение к сложным функциям, а также в качестве "сокращенной формы" перегрузки функций.

Задание аргументов, передаваемых функции по умолчанию, синтаксически аналогично инициализации переменных. Рассмотрим следующий пример, в котором объявляется функция myfunc(), принимающая один аргумент типа double с действующим по умолчанию значением 0.0 и один символьный аргумент с действующим по умолчанию значением 'Х'.

void myfunc(double num = 0.0, char ch = 'Х')

{

 .

 .

 .

}

После такого объявления функцию myfunc() можно вызвать одним из трех следующих способов.

Myfunc(198.234, 'A'); // Передаем явно заданные значения.

Myfunc(10.1); // Передаем для параметра num значение 10.1, а для параметра ch позволяем применить аргумент, задаваемый по умолчанию ('Х').

Myfunc(); // Для обоих параметров num и ch позволяем применить аргументы, задаваемые по умолчанию.

При первом вызове параметру num передается значение 198.234, а параметру ch — символ 'А'. Во время второго вызова параметру num передается значение 10.1, а параметр ch по умолчанию устанавливается равным символу 'Х'. Наконец, в результате третьего вызова как параметр num, так и параметр ch по умолчанию устанавливаются равными значениям, заданным в объявлении функции.

Включение в C++ возможности передачи аргументов по умолчанию позволяет программистам упрощать код программ. Чтобы предусмотреть максимально возможное количество ситуаций и обеспечить их корректную обработку, функции часто объявляются с большим числом параметров, чем необходимо в наиболее распространенных случаях. Поэтому благодаря применению аргументов по умолчанию программисту нужно указывать не все аргументы (используемые в общем случае), а только те, которые имеют смысл для определенной ситуации.

Аргумент, передаваемый функции по умолчанию, представляет собой значение, которое будет автоматически передано параметру функции в случае, если аргумент, соответствующий этому параметру, явным образом не задан.

Насколько полезна возможность передачи аргументов по умолчанию, показано на примере функции clrscr(), представленной в следующей программе. Функция clrscr() очищает экран путем вывода последовательности символов новой строки (это не самый эффективный способ, но он очень подходит для данного примера). Поскольку в наиболее часто используемом режиме представления видеоизображений на экран дисплея выводится 25 строк текста, то в качестве аргумента по умолчанию используется значение 25. Но так как в других видеорежимах на экране может отображаться больше или меньше 25 строк, аргумент, действующий по умолчанию, можно переопределить, явно указав нужное значение.

#include <iostream>

using namespace std;

void clrscr(int size=25);

Int main()

{

 int i;

 for(i=0; i<30; i++ ) cout << i << '';

 clrscr(); // Очищаем 25 строк.

 for(i=0; i<30; i++ ) cout << i << '';

 clrscr(10); // Очищаем 10 строк.

 return 0;

}

Void clrscr(int size)

{

 for(; size; size--) cout << '';

}

Как видно из кода этой программы, если значение, действующее по умолчанию, соответствует ситуации, при вызове функции clrscr() аргумент указывать не нужно. Но в других случаях аргумент, действующий по умолчанию, можно переопределить и передать параметру size нужное значение.

При создании функций, имеющих значения аргументов, передаваемых по умолчанию, необходимо помнить две вещи. Эти значения по умолчанию должны быть заданы только однажды, причем при первом объявлении функции в файле. В предыдущем примере аргумент по умолчанию был задан в прототипе функции clrscr(). При попытке определить новые (или даже те же) передаваемые по умолчанию значения аргументов в определении функции clrscr() компилятор отобразит сообщение об ошибке и не скомпилирует вашу программу.

Несмотря на то что передаваемые по умолчанию аргументы должны быть определены только один раз, для каждой версии перегруженной функции для передачи по умолчанию можно задавать различные аргументы. Таким образом, разные версии перегруженной функции могут иметь различные значения аргументов, действующие по умолчанию.

Важно понимать, что все параметры, которые принимают значения по умолчанию, должны быть расположены справа от остальных. Например, следующий прототип функции содержит ошибку.

// Неверно!

void f(int а = 1, int b);

Если вы начали определять параметры, которые принимают значения по умолчанию, нельзя после них указывать параметры, задаваемые при вызове функции только явным образом. Поэтому следующее объявление также неверно и не будет скомпилировано.

int myfunc(float f, char *str, int i=10, int j);

Поскольку для параметра i определено значение по умолчанию, для параметра j также нужно задать значение по умолчанию.

Сравнение возможности передачи аргументов по умолчанию с перегрузкой функций

Как упоминалось в начале этого раздела, одним из применений передачи аргументов по умолчанию является "сокращенная форма" перегрузки функций. Чтобы понять это, представьте, что вам нужно создать две "адаптированные" версии стандартной функции strcat(). Одна версия должна присоединять все содержимое одной строки к концу другой. Вторая же принимает третий аргумент, который задает количество конкатенируемых (присоединяемых) символов. Другими словами, эта версия должна конкатенировать только заданное количество символов одной строки к концу другой.

Допустим, что вы назвали свои функции именем mystrcat() и предложили такой вариант их прототипов.

void mystrcat(char *s1, char *s2, int len);

void mystrcat(char *s1, char *s2);

Первая версия должна скопировать len символов из строки s2 в конец строки s1. Вторая версия копирует всю строку, адресуемую указателем s2, в конец строки, адресуемой указателем s1, т.е. действует подобно стандартной функции strcat().

Несмотря на то что для достижения поставленной цели можно реализовать две версии функции mystrcat(), есть более простой способ решения этой задачи. Используя возможность передачи аргументов по умолчанию, можно создать только одну функцию mystrcat(), которая заменит обе задуманные ее версии. Реализация этой идеи продемонстрирована в следующей программе.

// Применение пользовательской версии функции strcat().

#include <iostream>

#include <cstring>

using namespace std;

void mystrcat(char *s1, char *s2, int len = -1);

Int main()

{

 char str1[80] = "Это тест.";

 char str2[80] = "0123456789";

 mystrcat(str1, str2, 5); // Присоединяем 5 символов.

 cout << str1 << '';

 strcpy(str1, "Это тест."); // Восстанавливаем str1.

 mystrcat(str1, str2); // Присоединяем всю строку.

 cout << str1 << '';

 return 0;

}

// Пользовательская версия функции strcat().

void mystrcat(char *s1, char *s2, int len)

{

 // Находим конец строки s1.

 while(*s1) s1++;

 if(len == -1) len = strlen(s2);

 while(*s2 && len) {

  *s1 = *s2; // Копируем символы.

  s1++; s2++; len--;

 }

 *s1 = '0'; // Завершаем строку s1 нулевым символом.

}

Здесь функция mystrcat() присоединяет len символов строки, адресуемой параметром s2, к концу строки, адресуемой параметром s1. Но если значение len равно -1, как и в случае разрешения передачи этого аргумента по умолчанию, функция mystrcat() присоединит к строке s1 всю строку, адресуемую параметром s2. (Другими словами, если значение len равно -1, функция mystrcat() действует подобно стандартной функции strcat().) Используя для параметра len возможность передачи аргумента по умолчанию, обе операции можно объединить в одной функции.

Этот пример позволил продемонстрировать, как аргументы, передаваемые функции по умолчанию, обеспечивают основу для сокращенной формы объявления перегруженных функций.

Об использовании аргументов, передаваемых по умолчанию

Несмотря на то что аргументы, передаваемые функции по умолчанию, — очень мощное средство программирования (при их корректном использовании), с ними могут иногда возникать проблемы. Их назначение — позволить функции эффективно выполнять свою работу, обеспечивая при всей простоте этого механизма значительную гибкость. В этом смысле все передаваемые по умолчанию аргументы должны отражать способ наиболее общего использования функции или альтернативного ее применения. Если не существует некоторого единого значения, которое обычно присваивается тому или иному параметру, то и нет смысла объявлять соответствующий аргумент по умолчанию. На самом деле объявление аргументов, передаваемых функции по умолчанию, при недостаточном для этого основании деструктуризирует код, поскольку такие аргументы способны сбить с толку любого, кому придется разбираться в такой программе. Наконец, основным принципом использования аргументов по умолчанию должен быть, как у врачей, принцип "не навредить". Другими словами, случайное использование аргумента по умолчанию не должно привести к необратимым отрицательным последствиям. Ведь такой аргумент можно просто забыть указать при вызове некоторой функции, и, если это случится, подобный промах не должен вызвать, например, потерю важных данных!









Читайте также:

Последнее изменение этой страницы: 2016-03-17; Просмотров: 97;


lektsia.info 2017 год. Все права принадлежат их авторам! Главная