При закрытии подписчики были переданы в рассылку "Программирование на Visual С++" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Информационный Канал Subscribe.Ru |
C++ для всех. Выпуск 2 Функции. Параметры функций. Шаблоны функций |
Функции представляют собой наборы действий над данными. При использовании функций существуют два понятия: объявление и определение. Определяться функция может только один раз, объявляться - сколько угодно. При объявлении может использоваться ключевое слово extern. Соответственно, определяться функция может в одном файле, а объявляться и использоваться - в нескольких. При объявлении имена параметров можно не писать, - учитываются только типы. При определении имена переменных, которые не используются (на случай использования в будущем или для совместимости), можно не писать.
extern func(int); //объявление void f(){ func(10); } void func(int val){ std::cerr << val << '\n'; } //определение int main() { f(); return 0; }
Функция может возвращать значение. Если возвращаемого значения нет, то перед именем функции ставят ключевое слово void. Если тип возвращаемого значения отличается от объявленного, то происходит неявное преобразование в объявленный тип, при невозможности данного преобразования компиляция будет прервана с сообщением об ошибке.
void func(){ ... } void func(){ return 0; } //ошибка int func(){ return 10; }
Функция может определяться как встроенная (inline). Тело данных функций, по возможности, встраивается в место вызова, этим может достигаться определенное увеличение скорости работы программы за счет отсутствия пролога и эпилога функций, которые атоматически генерирует компилятор. Такая функция должна быть определена с ключевым словом inline, причем определение должно находиться в области видимости того места, откуда ее вызывают. Как правило, inline-функции определяют в заголовочных (*.h) файлах.
inline void fast_inc(int &val){ val++; } int main() { int i = 0; fast_inc(i); return 0; }
Функция может содержать параметры, которые перечисляются через запятую. Компилятор проводит неявное преобразование типов при несоответствии передаваемого типа объявленному, при неудаче будет выдано сообщение об ошибке. В объявлении функции можно определить значения параметров по умолчанию,- данные значения будут автоматически подставлены компилятором при вызове функции без соответствующего параметра.
Существует несколько способов передачи параметров в функцию: -через глобальные переменные. Данный способ нельзя рекомендовать к повсеместному использованию, т.к. глобальные переменные могут находиться в достаточно "обширной" области видимости, и соответственно, могут изменяться многими конструкциями программы. Программа становится не просто некрасивой, а потенциально уязвимой возможными ошибками. Если возникает необходимость пользоваться глобальными переменными, то лучше объявлять такие переменные как static,- это ограничит область видимости этих переменных до зоны данного файла (эта рекомендация, естественно, для случая, когда подобные переменные не нужны в других файлах).
static int gvalue; ... void inc_value() { gvalue++; } int main() { inc_value(); return 0; }
int inc_value(int v){ return ++v; } int main() { int i=0; int rez = inc_value(i); //rez теперь равен 1 //i как и прежде равен 0 return 0; }
void inc_value(int &v){ v++; } int main() { int i=0; inc_value(i); //i теперь равен 1 char c = 0; inc_value(c); //ошибка inc_value(10); //ошибка ... return 0; }
void process_value(const int &v){ ... } int main() { int i=0; process_value(i); process_value(10); //нормально char c = 0; process_value(c); //нормально ... return 0; }
void inc_value(int *pval){ if(pval) ++(*pval); } int main() { int i=0; inc_value(&i); //i теперь равен 1 return 0; }
int inc_value(const int *pval) { //++(*pval); //ошибка, - разрешено только для чтения return *pval+1; } int main() { int i=0; i = inc_value(&i); //i теперь равен 1 return 0; }
Итог: для передачи больших объектов применяйте ссылки, константные ссылки, указатели и константные указатели, для простых объектов, возвращаемое значение которых нас не интересует, применяйте способ передачи параметров по значению.
Имена функций могут совпадать. Данный механизм называется перегрузкой. Однако передаваемые параметры функций должны различаться (возвращаемый параметр функции не учитывается). При вызове соответствующих функций любая неоднозначность трактовки входных параметров будет расцениваться как ошибочная. Например:
void f(int val1,int val2=0){} ... f(10); //ошибка, - какая функция должна быть вызвана? //------------------------------------ void f(int val1){} void f(const int val1){} //нельзя
Не рекомендуется передавать в перегруженную функцию параметры, которые могут неявно быть преобразованы к типам обеих (или более) перегруженных функций, в этом случае параметры лучше преобразовать явно.
void f(double val){} void f(int val){} ... char c = 0; f(c); //нерекомендуемо f(int(c)); f(double(c));
Шаблоны функций
Данная тема довольно обширна и подробно будет рассмотрена при обсуждении классов, поэтому здесь я кратко остановлюсь на самом понятии шаблонов, где и для чего они применяются, а также приведу пару примеров использования шаблонов в функциях.
Итак, шаблоны - это механизм, который используется для автоматической генерации множества функций (классов), которые отличаются только типами. Все определенные на этапе разработки действия над объектами остаются неизменными. Что это нам дает? А то, что мы можем написать, например, функцию сортировки, которая сможет сортировать либой тип, который мы ей дадим. В языке C подобный механизм реализовывался через указатель void*, который явно приводился в необходимому типу во время использования разработанного сообщества функций, однако данный способ является потенциально способствующий возникновению ошибок, т.к. подобное явное приведение типов компилятором не контролируется. Определенным неудобством использованием шаблонов является то, что реализация шаблонной фунции(класса) должна быть проведена в одном файле, но, особых проблем это вызывает, т.к. при необходимости разработки сложных иерархий, определение базовых классов можно провести без шаблонов, а шаблонную надстройку добавить в самом конце иерархии,- получим закрытый базовый "черный ящик" с шаблонным внешним интерфейсом.
templatevoid inc_value(T &val) { val++; } int main() { int x = 0; inc_value (x); char c = 0; inc_value (c); return 0; } Пример шаблонной функции быстрой сортировки template void QuickSort(T A[], int low, int high) { T pivot; int scanUp, scanDown; int mid; if (high - low <= 0) return; else if (high - low == 1) { if (A[high] < A[low]) std::swap(A[low], A[high]); return; } mid = (low + high)/2; pivot = A[mid]; std::swap(A[mid], A[low]); scanUp = low + 1; scanDown = high; do{ while (scanUp <= scanDown && A[scanUp] <= pivot) scanUp++; while (pivot < A[scanDown]) scanDown--; if (scanUp < scanDown) std::swap(A[scanUp], A[scanDown]); } while (scanUp < scanDown); A[low] = A[scanDown]; A[scanDown] = pivot; if (low < scanDown-1) QuickSort (A, low, scanDown-1); if (scanDown+1 < high) QuickSort (A, scanDown+1, high); }
На этом закончим второй выпуск. Пожелания, предложения, вопросы прошу на iqsoft@cg.ukrtel.net
В следующем выпуске: Структуры. Классы. Иерархия классов.
(с) Юрий Гордиенко iqsoft@cg.ukrtel.net
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||