Один из способов, которым достигается реализация полиморфизма в языке C++, заключается в использовании перегрузки функций. В C++ две или более функции могут иметь одно и то же имя в случае, если они отличаются набором параметров. В таком случае о функциях говорят, что они перегружены. Рассмотрим в качестве примера следующую программу:
#include <iostream.h>
int sqr(int i);
double sqr(double d);
long sqr(long l);
int main()
{
cout << sqr(10) << "\n";
cout << sqr(11.0) << "\n";
cout << sqr(9L) << "\n";
return 0;
}
int sqr(int i)
{
cout << "Inside the sqr() function that uses an integer argument.\n";
return i*i;
}
double sqr(double d)
{
cout << "Inside the sqr() function that uses a double argument.\n";
return d*d;
}
long sqr(long l)
{
cout << "Inside the sqr() function that uses a long argument.\n";
return l*l;
}
В этой программе создаются три сходные, но, однако, различные функции с именами sqr(), каждая из которых возвращает квадрат своего аргумента. Как показывает эта программа, компилятор знает, какую функцию использовать, благодаря типу аргумента. Значение перегруженных функций заключается в том, что они позволяют обеспечивать доступ к целому набору функций, используя общее имя. По существу перегрузка функций позволяет создавать единое имя для операции, а компилятор устанавливает, какую именно функцию надо использовать в конкретной ситуации для выполнения операции.
Перегрузка функций важна потому, что она помогает в решении проблемы сложности. Рассмотрим следующий пример, иллюстрирующий, как это происходит. Стандартная библиотека C содержит функции itoa(), ltoa() и ultoa(). Все эти функции преобразуют числа различных типов (целые, длинные целые и беззнаковые целые) в их строковые эквиваленты. Хотя эти функции выполняют почти что одинаковые действия, в языке С для выполнения такого преобразования необходимо использовать три различных имени, что делает ситуацию более сложной, чем она есть на самом деле. Хотя основная концепция каждой функции одна и та же, программист вынужден запоминать и работать с каждой функцией отдельно. В противоположность этому в C++ можно было бы использовать одно и то же имя для каждой из трех функций. Это имя выражало бы общие действие, которые необходимо выполнить. Задача выбора специфической версии функции возлагается на компилятор. Этот выбор зависит от конкретной ситуации.
В следующем примере иллюстрируется практическая ситуация, в которой перегрузка функций оказывает существенную помощь. Языки С и C++ не содержат библиотечных функций, которые выдают подсказку пользователю, чтобы он ввел данные, а затем ждут от него ответа. В представленной ниже программе создаются три такие функции, с именем prompt(), осуществляющие ввод данных типов int, double и char*, т.е. строк:
#include <iostream.h>
void prompt(char *str, int *i);
void prompt(char *str, double *d);
void prompt(char *str, char *dest);
int main()
{
int i;
double d;
char s[80];
prompt("Enter an integer: ", &i);
prompt("Enter a double: ", &d);
prompt("Enter a string: ", s);
cout << i << " " << d << " " << s;
return 0;
}
void prompt(char *str, int *i)
{
cout << str;
cin >> *i;
}
void prompt(char *str, double *d)
{
cout << str;
cin >> *d;
}
void prompt(char *str, char *dest)
{
cout << str;
cin >> dest;
}
Хотя можно использовать одно и то же имя для перегрузки функций, выполняющих совершенно различные задачи, делать этого не следует. Например, можно использовать имя sqr() для создания функций, возвращающих квадрат целых чисел и квадратный корень чисел типа double. Тем не менее, эти операции совершенно различны и использование перегрузки функций в подобных ситуациях противоречит самой идее перегрузки функций. В частности, перегрузку функций следует применять только для сходных между собой операций.