- •Глава 12
- •Конструкторы и деструкторы. Список инициализации элементов
- •Конструкторы по умолчанию и конструкторы копирования
- •Указатель this
- •Встраиваемые (inline-) функции
- •Статические члены класса
- •Использование указателей на функции-члены класса
- •Массивы объектов класса
- •Дружественные функции и друзья класса
- •Глава 13
- •Множественное наследование
- •Виртуальные базовые классы
- •Глава 14
- •Перегрузка функций
- •Перегрузка конструкторов
- •Создание и использование конструкторов копирования
- •Определение адреса перегруженной функции
Глава 14
перегрузка функций
Почему следует использовать перегрузку
Перегрузка функций - это один из типов полиморфизма, обеспечиваемого C++. В C++ несколько функций могут иметь одно и то же имя. В этом случае функция, идентифицируемая этим именем, называется перегруженной. Перегрузить можно только функции, которые отличаются либо типом, либо числом своих аргументов. Перегрузить функции, которые отличаются только типом возвращаемого значения, нельзя. Возникает вопрос: зачем нужно использовать перегрузку? Ответ прост: перегружаемые функции дают возможность упростить программы, допуская обращение к одному имени для выполнения близких по смыслу действий.
Чтобы перегрузить некоторую функцию, нужно просто объявить, а затем определить все требуемые варианты ее вызова. Компилятор автоматически выберет правильный вариант вызова на основании числа и типа используемых аргументов. Пример:
#include <iostream.h>
//Перегрузка функции abs()
int abs(int n);
long abs(long 1);
double abs(double d);
main()
{
int n=255; long l=-25L; double d=-15.0L;
cout « "Абсолютная величина " « n «" равна: " « abs(n) « "\n";
cout « "Абсолютная величина " « l «" равна: " « abs(l) « "\n";
cout « "Абсолютная величина” « d «" равна: " « abs(d) « "\n";
return 0;
}
int abs(int n)
{
return n<0? -n:n;
}
long abs(long l)
{
return l<0? -l:l;
}
double abs(double d)
{
return d<0? -d:d;
}
Как известно, библиотека времени выполнения C++ использует три разных функции abs (), labs () и fabs () для вычисления абсолютного значения аргумента. Использование той или другой из них зависит от типа аргумента. Так, в данном примере осуществляется перегрузка функции abs (), что упрощает программу. В зависимости от переданного аргумента вызывается нужный вариант функции.
Этот пример наглядно демонстрирует, как использование перегрузки может упростить код, предоставляя программисту возможность пользоваться одной функцией, которой передаются аргументы различного типа. Разумеется, возможность использования перегрузки предъявляет повышенные требования к компилятору.
Чтобы реализовать концепцию перегрузки, разработчикам компиляторов C++ пришлось ввести декорирование имен. Последнее означает, что все функции в коде программы получают от компилятора имена, основываясь на имени, заданном программистом, и количестве и типах аргументов. Различные компиляторы делают это несколько отличным друг от друга образом. Здесь мы опишем, как это делает компилятор фирмы Inprise (ранее - фирма Borland): вначале идет имя класса (если речь идет о функции-члене класса), предваряемое символом "@", затем имя функции, снова предваряемое символом "@". У всех идентификаторов различается регистр букв. Затем следует последовательность символов "@q", начиная с которой идут кодированные обозначения параметров функции. Для обозначения указателей и ссылок к кодам встроенных типов добавляются буквы "р" и "r", соответственно. Например, если дано такое определение класса:
class AnyClass
{
public:
void SetVal() ;
void SetVal(int);
void SetVal(int, int);
void SetVal(int, int, int);
void SetVal(int&, int, int);
};
компилятор Borland C++ сгенерирует такие имена функций:
@AnyClass@SetVal@qv
@AnyClass@SetVal@qi
@AnyClass@SetVal@qii
@AnyClass@SetVal@qiii
@AnyClass@SetVal@qriii
Подробнее о декорировании имен, в частности о кодировании имен конструкторов и деструкторов, а также имен, указанных через операцию разрешения области видимости, можно прочесть в документации, поставляемой с компилятором.
Отсюда видно, что в декорировании имен возвращаемое функцией значение не участвует. Именно по этой причине нельзя перегружать функции, которые отличаются только типом возвращаемого значения. Разобраться с тем, как декорирует имена конкретный компилятор достаточно легко. Достаточно потребовать от него выдать ассемблерный эквивалент текста программы.
Замечание. Декорирование имен порождает и определенные проблемы. Если нужно написать код, который будет вызываться из программы, написанной на другом языке или даже на другом компиляторе C++, нужно отключить декорирование имен, иначе нужная функция не будет найдена. Это делается с помощью ключевого слова extern в форме extern "С" {} Буква "С" здесь напоминает о языке С, в котором декорирования имен не было.
