- •Глава 12
- •Конструкторы и деструкторы. Список инициализации элементов
- •Конструкторы по умолчанию и конструкторы копирования
- •Указатель this
- •Встраиваемые (inline-) функции
- •Статические члены класса
- •Использование указателей на функции-члены класса
- •Массивы объектов класса
- •Дружественные функции и друзья класса
- •Глава 13
- •Множественное наследование
- •Виртуальные базовые классы
- •Глава 14
- •Перегрузка функций
- •Перегрузка конструкторов
- •Создание и использование конструкторов копирования
- •Определение адреса перегруженной функции
Перегрузка функций
Перегрузка - это практика предоставления более чем одного определения для данного имени функции в одной и той же области видимости. Возможность выбрать соответствующую версию функции, основываясь на типах и числе аргументов, с которыми она вызывается, предоставляется компилятору. При этом два типа данных считаются различными, если для них используются различные инициализаторы. Поэтому аргумент данного типа и ссылка на этот тип рассматриваются как одно и то же с точки зрения перегрузки. Например, объявление двух таких функций
int func(int, int)
и
int func(int&, int&)
приведет к ошибке, так как с точки зрения перегрузки они считаются одинаковыми. По той же причине аргументы функции, относящиеся к некоторому типу, модифицированные const или volatile, не рассматриваются как отличные от базового типа с точки зрения перегрузки. Указатели на const- и volatile-объекты также не рассматриваются как отличные от указателей на базовый тип с точки зрения перегрузки. Однако механизм перегрузки может различать ссылки, которые имеют модификаторы const и volatile, и ссылки на базовый тип. Например, следующий код вполне допустим:
#include <iostream.h>
class AnyClass
{
public:
AnyClass()
{cout «"Конструктор по умолчанию "«"для AnyClass" « "\n";} AnyClass(AnyClassS ob)
{cout «"Конструктор копирования для " « "AnyClass" « "\n";}
AnyClass(const AnyClassS ob)
{cout «"Конструктор копирования для" « "AnyClass" « "\n";}
};
main()
{
AnyClass ob1;
AnyClass оb2(ob1);
const AnyClass оb3;
AnyClass оb4(оb3);
}
Вместе с тем на перегруженные функции накладываются несколько ограничений:
• любые две перегруженные функции должны иметь различные списки параметров;
• перегрузка функций с совпадающими списками аргументов на основе лишь типа возвращаемых ими значений недопустима;
• функции-члены не могут быть перегружены исключительно на основе того, что одна из них является статической, а другая - нет;
• typedef-определения не влияют на механизм перегрузки, так как они не вводят новых типов данных, а определяют лишь синонимы для существующих типов. Например, следующее определение
typedef char* PSTR;
не позволит компилятору рассматривать две приведенные ниже функции –
void SetVal(char* sz) ;
и
void SetVal(PSTR sz);
как различные. Поэтому их одновременное объявление в классе вызовет ошибку;
• все enum-типы данных рассматриваются как различные и могут использоваться для различения перегруженных функций;
• типы "массив (чего-то)" и "указатель (на что-то)" рассматриваются как идентичные с точки зрения перегрузки. Это верно только для одномерных массивов. Поэтому в случае перегрузки следующих функций будет выдана ошибка:
void SetVal(char sx);
void SetVal(char* psz);
Для многомерных массивов вторая и последующие размерности рассматриваются как часть типа данных. Поэтому они могут использоваться для различения перегруженных функций. Например, вполне допустимы следующие определения:
void SetVal(char szStr[]);
void SetVal(char szStr[][4]);
При объявлении перегруженных функций компилятор отслеживает объявления, данные в пределах одной и той же области видимости. Поэтому, если в производном классе объявлена функция с тем же именем, что и функция в базовом классе, функция из производного класса скрывает функцию базового класса вместо того, чтобы осуществлять перегрузку (так как их области видимости различны). Короче говоря, нельзя путать переопределение функций с перегрузкой. Точно также компилятор отслеживает и другие области видимости. Поэтому, так как функция, объявленная в области видимости файла, находится не в той же области видимости, что и функция, объявленная в блоке, даже если они имеют одинаковые имена, компилятор не рассматривает их как перегруженные. Локально объявленная функция просто скрывает глобально объявленную. Например:
#include <iostream.h>
void func(int i)
{
cout « "Вызов глобально объявленной "«"функции:"« i « endl;
}
void func(char* str)
{
cout «"Вызов локально объявленной " « "функции:" « str « endl;
}
main ()
{
extern void func(char*);
//Следующий вызов функции вызовет ошибку
func(100);
//А это - верно
func("переопределение!");
return 0;
}
Перегруженные функции-члены могут быть объявлены с различными спецификаторами доступа. Поскольку область видимости у них по-прежнему одна, компилятор рассматривает функции-члены класса с одинаковыми именами, даже если они объявлены в разделах класса с разными спецификаторами доступа, как перегруженные. Например:
class AnyClass
{
public:
AnyClass();
double MembFunc(double, char*);
private:
int MembFunc(int);
};
double
AnyClass:: MembFunc(double, char*)
{
//Тело функции
}
int
AnyClass:: MembFunc(int)
{
//Тело функции
}
main()
{
AnyClass* pCl = new AnyClass;
//Следующая инструкция вызовет ошибку, т.к. она содержит вызов private-функции
рСl-> MembFunc(104);
//А здесь - все верно, т.к. вызывается publiс-функция
рСl-> MembFunc(104.22, "строковый аргумент");
delete pCl;
return 0;
}
Концептуально можно перегружать и статические функции-члены класса, хотя это не имеет большого смысла. Ниже приведен пример перегруженной статической функции-члена.
class AnyClass
{
public:
static void SetVal(int);
static void SetVal(double);
private:
static int x;
static double y;
};
int AnyClass::x = 0;
double AnyClass::у = 0;
void AnyClass::SetVal(int _x){x = _x;}
void AnyClass::SetVal(double _y){y = _y;}
main ()
{
AnyClass::SetVal(10);
AnyClass::SetVal(10.4567);
return 0;
}
