Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.




Перегрузка функций, шаблоны в C.

Перегрузка функций

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

Компилятор определяет, какую именно функцию требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует.

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

Пример 1. Перегрузка функций. int max(int, int); /* Возвращает наибольшее из двух целых */ char* max(char*, char*); /* Возвращает подстроку наибольшей длины */ int max (int, char*); /* Возвращает наибольшее из первого параметра и длины второго */ int max (char*, int); /* Возвращает наибольшее из второго параметра и длины первого */ void f(int a, int b, char* c, char* d){ cout << max (a, b) << max(c, d) << max(a, c) << max(c, b); }

В том случае, если точного соответствия не было найдено:

          • выполняются продвижения порядковых типов в соответствии с общими правилами преобразования типов; (например, bool и char в int, float в double и т. п.)
          • выполняются стандартные преобразования типов; (например, int в double или указателей в void*)
          • выполняются преобразования типа, заданные пользователем, а также поиск соответствий за счет переменного числа аргументов функций.

Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.

Неоднозначность может появиться в следующих случаях:

          • при преобразовании типа;
          • при использовании параметров-ссылок;
          • при использовании аргументов по умолчанию.
Пример 2. Неоднозначность при преобразовании типов. #include <iostream.h> float f(float i){ cout << "function float f(float i)" << endl; return i; } double f(double i){ cout << "function double f(double i)" << endl; return i*2; } int main(){ float x = 10.09; double y = 10.09; cout << f(x) << endl; // Вызывается f(float) cout << f(y) << endl; // Вызывается f(double) cout << f(10) << endl; /*Неоднозначность - как преобразовать 10: во float или double? */ return 0; }

Для устранения неоднозначности требуется явное приведение типа для константы 10.

Если одна из перегружаемых функций объявлена как int f(int a, int b), а другая — как int f (int a, int &b), компилятор не сможет узнать, какая из этих функций вызывается.

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

Пример 3. Неоднозначность при использовании аргументов по умолчанию. #include <iostream.h> int f(int a){ return a; } int f(int a, int b = 1){ return a * b; } int main(){ cout << f(10,2); // Вызывается f(int, int) cout << f(10); /* Неоднозначность - что вызывается: f(int, int) или f(int)? */ return 0; }

Правила описания перегруженных функций:

    1. перегруженные функции должны находиться в одной области видимости, иначе произойдет перекрытие аналогично одинаковым именам переменных во вложенных блоках;
    2. перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать;
    3. в различных вариантах перегруженных функций может быть различное количество параметров по умолчанию;
    4. функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и const int или int и int&).

Шаблоны функций

Многие алгоритмы не зависят от типов данных, с которыми они работают (классический пример — сортировка), и порой это может создавать некоторые трудности. Соответственно, возникает желание, разработать алгоритм таким образом, чтобы он был применим для различных типов данных. В таком случае в голову приходит несколько вариантов решения поставленной задачи:

    1. передача информации о типе в качестве параметра;
    2. создание нескольких перегруженных функций;
    3. использование шаблонов.

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

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

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

Создание шаблонасоздание функции, которая автоматически перегружает сама себя и при этом не влечёт никаких накладных расходов, связанных с параметризацией.

Различают шаблоны функций и шаблоны классов.

Синтаксис простейшего шаблона функции имеет вид:

template <class Type> заголовок{ /* тело функции */ }

Вместо слова Туре может использоваться произвольный идентификатор.

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

template <class A, class В, int i> void f(){... }

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

Задача: создать функцию, сортирующую массив из n элементов любого типа методом выбора.

Пример 4. Создание шаблона. template<class Type> voidsort_vybor(Type*b, intn){ Typeа; //буферная переменная для обмена элементов for(inti = 0; i<n-1; i++){ intimin = i; for(intj = i + 1; j<n; j++) if(b[j] < b[imin]) imin = j; а = b[i]; b[i] = b[imin]; b[imin] = a; } }
Пример 5. Использование созданного шаблона. #include <iostream.h> template <class Type> void sort_vybor(Type *b, int n); int main(){ const int n = 20; int i, b[n]; for (i = 0; i<n; i++) cin >> b[i]; sort_vybor(b, n); // Сортировка целочисленного массива for (i = 0; i<n; i++) cout << b[i] << ' '; cout << endl; double a[] = {0.22, 117, -0.08, 0.21, 42.5}; sort_vybor(a, 5); // Сортировка массива вещественных чисел for (i = 0; i<5; i++) cout << a[i] << ' '; return 0; }

Действия компилятора при использовании шаблонов:

    1. Первый вызов функции, который использует конкретный тип данных, приводит к созданию компилятором кода для соответствующей версии функции. Этот процесс носит название «инициализация шаблона».
    2. Конкретный тип для инстанцирования либо определяется компилятором автоматически, исходя из типов параметров при вызове функции, либо задается явным образом.
    3. При повторном вызове с тем же типом данных код заново не генерируется.
Пример 6. Явное задание аргументов шаблона при вызове. template <class X, class Y, class Z> void f(Y, Z); void g(){ f<int, char*, double>("Vasia", 3.0); f<int, char*>("Vasia", 3.0); // Z определяется как double f<int>("Vasia", 3.0); // Y определяется как char*, a Z - как double // f("Vasia", 3.0); - ошибка: Х определить невозможно }


Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-06-05 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: