Урок 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. Вызов функций при перегрузке

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

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

Возможны три случая:

  1. Имеет место точное совпадение;

При этом char, short, 0 точно соответствуют типу int;

  1. Соответствие достигается при помощи преобразований;

При этом:

  • фактический аргумент любого числового типа может быть приведен к любому числовому типу;
  • константа 0 типа может быть преобразована к указателю;
  • любой указатель может быть преобразован к void *;
  • соответствие может быть достигнуто при помощи преобразований пользователя (в применении к фактическому параметру), т.е., если нет стандартных преобразований, тогда компилятор пытается применить преобразования определенные пользователем в классе.
  1. Соответствие достигается с более чем одним экземпляром функции, в этом случае выдается сообщение об ошибке. Например:

 

   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. Перегрузка и область видимости

Здесь действует два простых правила:

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

 

рассказать друзьям и получить подарок

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов