Урок 9.7. Перегрузка функций
9.7. Перегрузка функций
9.7.1. Вызов функций при перегрузке
9.7.2. Перегрузка функций с несколькими аргументами
9.7.3. Перегрузка функций с аргументами по умолчанию
9.7.4. Перегрузка и область видимости
9.7. Перегрузка функций
Что такое перегрузка функций? В С++ несколько функций могут иметь одинаковые имена. Эта возможность используется, в основном, когда надо выполнить похожие действия, но над данными различных типов. Например:
int min(int, int);
long min(long, long);
double min(double, double);
Вызов функции min(): double a = min(34.78, 88.77); Компилятор сам определит, какой конкретно экземпляр функции вызвать. Такой механизм называется перегрузкой функции.
Будем называть сигнатурой функции тип и количество параметров, тип возвращаемого значения и имена параметров значения не имеют.
Функции не считаются перегруженными если:
- сигнатуры совпадают, в этом случае второе и все остальные объявления трактуются как переобъявления первого;
- сигнатуры функций совпадают, но возвращаемые значения различны, в этом случае последующие объявления считаются ошибочными;
int min(int, int);
long min(int, int); //Error
Если сигнатуры различны, тогда функция считается перегруженной, при условии, что её экземпляры объявлены в одной зоне видимости.
Чаще всего механизм перегрузки используется при объявлении конструкторов класса и при перегрузке операций.
9.7.1. Вызов функций при перегрузке
Функции, имеющие одинаковые имена и различные сигнатуры и определенные в одной области видимости, называются перегруженными.
Выбор экземпляра функции при вызове включает сравнение типов и числа фактических аргументов с формальными параметрами в объявлении каждого экземпляра.
Возможны три случая:
- Имеет место точное совпадение;
При этом char, short, 0 точно соответствуют типу int;
- Соответствие достигается при помощи преобразований;
При этом:
- фактический аргумент любого числового типа может быть приведен к любому числовому типу;
- константа 0 типа может быть преобразована к указателю;
- любой указатель может быть преобразован к void *;
- соответствие может быть достигнуто при помощи преобразований пользователя (в применении к фактическому параметру), т.е., если нет стандартных преобразований, тогда компилятор пытается применить преобразования определенные пользователем в классе.
- Соответствие достигается с более чем одним экземпляром функции, в этом случае выдается сообщение об ошибке. Например:
void f(long); void f(int); void f(double); unsigned int ui; |
Для вызова f(ui) есть 3 соответствия, каждое из которых достигается при помощи стандартного преобразования.
Пример. Применение комбинации преобразований – стандартных и определенных пользователем.
void f(void *); void f(double); class String{ // класс строк int sz; //длина строки char *str; //указатель на строку public: … //пользовательская перегруженная операция преобразования типа operator int(){ return sz;} }; Рассмотрим следующие ситуации: String s; f(5); //стандартное преобразование int->double, выбирается f(double). f(s); //1. преобразование пользователя в int, вызывается перегруженная // операция int //2. стандартное преобразование int->double, //3. выбирается f(double). Два уровня пользовательских преобразований не допускается. Примеры двусмысленных вызовов. 1. f(’a’); – ошибка, двусмысленная ситуация, точного соответствия нет, а символ ’a’ может быть преобразован и к long и к double, приоритетов преобразований нет. 2. class String{ Letter *str; public: // конструктор - преобразователь типа String(Letter); }; class Letter{ char ch; //преобразователь типа public: operator String(); } void f(String); Letter L; f(L); //Ошибка двусмысленности, в обоих классах есть преобразование //типа, компилятор затрудняется выбрать одно преобразование f(String(L)); //Ошибка двусмысленности, неизвестно, что вызывать //конструктор String или перегруженный оператор String. f(L.operator String()); //разрешение двусмысленной ситуации: //явный вызов операции преобразования типа |
В случае, когда одно из преобразований ведет к достижению точного соответствия, а другое требует ещё и стандартных преобразований, то двусмысленности не возникает.
9.7.2. Перегрузка функций с несколькими аргументами
При вызове функций с несколькими аргументами правила соответствия применяются для каждого аргумента. При перегрузке такой функции выбирается тот экземпляр, для которого достигнуто либо точное соответствие, либо достигнуто соответствие при помощи преобразований. Например:
void f(long *, int); void f(int, int); f(5, ’c’);//соответствует void f(int, int); |
В этом вызове, первый и третий параметры дают точное соответствие у обоих экземпляров функции. Второй аргумент вызываемой функции точно соответствует формальному параметру первого экземпляра, а для достижения соответствия со вторым экземпляром необходимо стандартное преобразование.
void f(int, double, long *); void f(int, long, void *); double a = 5, *pd; pd = &a; f(6, ’a’, pd);// соответствует void f(int, long, void *); |
В этом примере по первому и второму аргументу ни одна функция не имеет преимущества, но так как double * нельзя привести к long *, а к типу void * можно, то второй экземпляр функции имеет преимущество и будет вызван.
9.7.3. Перегрузка функций с аргументами по умолчанию
Если при перегрузке функций есть один или несколько экземпляров с аргументами по умолчанию, то вызываться будет тот, для которого обеспечены все или некоторое подмножество аргументов, к которым применимы правила соответствия. Например,
void f(int); void f(double, int = 0); f(5); //точно соответствует void f(int); f(5.7, 0); //точно соответствует void f(double, int = 0); f(7, 8); //соответствует void f(double, int ); //выбор по числу аргументов, 7 преобразуется в double f(34L); // ошибка: двусмысленность по параметру и нельзя //сделать выбор по числу аргументов f(34.56); //соответствует void f(double, int = 0); |
9.7.4. Перегрузка и область видимости
Здесь действует два простых правила:
- набор функций, объединенных общим именем, должен быть объявлен в одной области видимости для обеспечения перегрузки;
- локальное объявление функции с таким же именем, скрывает перегруженную функцию, объявленную на более высоком уровне с таким же именем.
Оставить комментарий