Перегрузка функций. Перегрузка функций в классах. Перегрузка конструкторов класса. Доступ к перегруженной функции по указателю. Примеры

Перегрузка функций. Перегрузка функций в классах. Перегрузка конструкторов класса. Доступ к перегруженной функции по указателю. Примеры


Содержание


1. Какие функции называются «перегруженными»? Что означает термин «перегрузка»?

«Перегрузка» функции – это объявление функции с тем же именем несколько раз. Таким образом, в некоторой области видимости имя «перегруженной» функции объявляется несколько раз. Чтобы компилятор мог отличать «перегруженные» функции между собой, эти функции должны отличаться списком входных параметров.

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

return_type1 FunName(parameters_list_1)
{
    // ...
}

return_type2 FunName(parameters_list_2)
{
    // ...
}

return_typeN FunName(parameters_list_N)
{
    // ...
}

где

  • FunName – имя перегруженной функции;
  • parameters_list1, parameters_list2, …, parameters_listN – списки параметров «перегруженной» функции с именем FunName;
  • return_type1, return_type2, …, return_typeN – типы параметров, которые возвращаются «перегруженной» функцией FunName. Компилятор различает «перегруженные» функции только по списку полученных параметров а не по возвращаемомоу значению.

2. По каким признакам отличаются перегруженные функции? Пример

Перегруженные функции отличаются по списку параметров. Списки параметров перегруженных функций должны отличаться по следующим признакам:

  • количеством параметров;
  • если количество параметров одинаковое, то по типам параметров.

Например. Функция Max() есть перегруженная и отличается количеством параметров и типами параметров

// функция Max с двумя параметрами типа int
int Max(int a, int b)
{
    // ...
}

// функция Max с тремя параметрами типа int
int Max(int a, int b, int c)
{
    // ...
}

// функция Max с двумя параметрами типа double
int Max(double a, double b)
{
    // ...
}

3. Примеры перегрузки функций

Пример 1. Функция Equal(), которая сравнивает два значения разных типов. Функция имеет три перегруженных реализации для разных типов (char, int, double).

Программный код, который демонстрирует использование функции Equal() следующий:

#include "stdafx.h"
#include <iostream>
using namespace std;

bool Equal(char c1, char c2)
{
    return (bool)(c1==c2);
}

bool Equal(double d1, double d2)
{
    return (bool)(d1==d2);
}

bool Equal(int i1, int i2)
{
    return (bool)(i1==i2);
}

int _tmain(int argc, _TCHAR* argv[])
{
    bool b;
    b = Equal(5,6); // вызывается Equal(int, int), b = 0
    b = Equal(3.755, 3.755); // вызывается Equal(double, double), b = 1
    b = Equal('A', 'A'); // вызывается Equal(char, char), b = 1
    return 0;
}

Пример 2. Функция Average(), которая находит среднее арифметическое значение. Функция имеет 4 перегруженных реализации для разного количества целочисленных параметров.

Программный код, демонстрирующий применение функции следующий:

#include "stdafx.h"
#include <iostream>
using namespace std;

double Average(int a, int b)
{
    return (a+b)/2.0;
}

double Average(int a, int b, int c)
{
    return (a+b+c)/3.0;
}

double Average(int a, int b, int c, int d)
{
    return (a+b+c+d)/4.0;
}

double Average(int a, int b, int c, int d, int e)
{
    return (a+b+c+d+e)/5.0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double avg;
    avg = Average(2,3); // avg = 2.5
    avg = Average(2,3,5); // avg = 3.3333
    avg = Average(2,3,5,8); // avg = 4.5
    avg = Average(2,3,5,8,11); // avg = 5.8
    return 0;
}

4. Пример перегрузки функции в классе

Пусть задан класс Day, реализующий день недели. В классе перегружена функция Set(), которая устанавливает новый день недели. Метод Set() имеет 2 реализации:

  • без параметров;
  • с одним параметром.

Программный код, демонстрирующий применение «перегрузки» функций в классе имеет следующий вид:

#include "stdafx.h"
#include <iostream>
using namespace std;

class Day
{
    int day;

    public:
    // метод Set() перегружен
    void Set(int nday) { day = nday; }
    void Set(void) { day = 1; }
    int Get(void) { return day; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Day D;
    int day;

    // использование перегруженного метода Set
    D.Set();
    day = D.Get(); // day = 1

    D.Set(5);
    day = D.Get(); // day = 5

    cout << day << endl;

    return 0;
}

5. Могут ли считаться перегруженными функции, которые имеют одинаковые имена, одинаковое количество и типы параметров, но которые возвращают значение разных типов?

Нет. Компилятор распознает перегруженные функции только по получаемым параметрам. Если две функции имеют одинаковые имена, одинаковое количество и типы параметров но возвращают разные значения, то такие функции считаются одинаковыми. В этом случае компилятор выдаст ошибку:

Например. В нижеследующем коде объявляются две функции с именем Inc5():

int Inc5(int d)
{
    return d+5;
}

double Inc5(int d)
{
    return (double)(d+5.0);
}

Как видно из примера, функции возвращают значение разных типов. Поскольку функции имеют одинаковые имена, одинаковое количество и типы параметров, то вышеприведенный код выдаст ошибку компилятора

'double Get5(int)' : overloaded function differs only by return type from 'int Get5(int)'

6. Для чего используется перегрузка конструкторов класса? Преимущества перегрузки конструкторов класса

Перегрузка конструкторов класса дает следующие преимущества:

  • повышение гибкости класса при его создании. С помощью перегрузки конструкторов пользователь выбирает оптимальный способ создания объекта. Если попробовать создать объект класса способом, для которого непредвиден конструктор, то компилятор выдаст сообщение об ошибке;
  • возможность создания инициализированных и неинициализированных объектов или объектов, которые инициализированы по умолчанию. В этом случае предполагается 2 варианта реализации конструктора: с инициализацией и без нее;
  • возможность создания конструкторов копирования.


7. Пример перегрузки конструкторов в классе

Пример. Задан класс Cylinder реализующий цилиндр. В классе перегружается конструктор, который может вызываться одним из трех способов:

  • конструктор без параметров;
  • конструктор с 1 параметром;
  • конструктор с 2 параметрами.
#include "stdafx.h"
#include <iostream>
using namespace std;

class Cylinder
{
    double r, h;

    public:
    // конструктор без параметров
    Cylinder()
    {
        r = 1;
        h = 1;
    }

    // конструктор с 1 параметром
    Cylinder(double h)
    {
        r = 1.0;
        this->h = h;
    }

    // конструктор с двумя параметрами
    Cylinder(double h, double r)
    {
        this->h = h;
        this->r = r;
    }

    // методы доступа
    double GetR(void) { return r; }
    double GetH(void) { return h; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Cylinder cl; // вызов конструктора без параметров
    Cylinder cl1(5); // вызов конструктора с 1 параметром
    Cylinder cl2(7, 9); // вызов конструктора с 2 параметрами
    double d;

    d = cl.GetH(); // d = 1
    d = cl1.GetH(); // d = 5
    d = cl2.GetH(); // d = 7

    cout << d << endl;
    return 0;
}

8. Каким образом осуществляется доступ к перегруженной функции с помощью указателя на функцию? Пример

При объявлении указателя на «перегруженную» функцию, компилятор определяет нужную функцию для указателя по его сигнатуре при объявлении.

Например. Пусть заданы 3 «перегруженные» функции Increment(), которые оперируют типами int, double, char. При объявлении указателя на функцию, нужно явным образом задать тип указателя при объявлении.

Нижеследующий код демонстрирует использование указателя на перегруженную функцию

#include "stdafx.h"
#include <iostream>
using namespace std;

int Increment(int i)
{
    return i+1;
}

double Increment(double d)
{
    return d+1;
}

char Increment(char c)
{
    return c+1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int (*pi)(int); // указатель на функцию, которая получает параметром int и возвращает int
    pi = Increment; // p указывает на int Increment(int)
    int d = (*pi)(4); // d = 5
    char (*pc)(char); // указатель на функцию, которая оперирует типом char
    pc = Increment; // p указывает на char Increment(char)
    char c = (*pc)('F'); // c = 'G'

    cout << c << endl;
    return 0;
}

9. Для чего используется ключевое слово overload?

Ключевое слово overload использовалось в ранних версиях C++ для указания того, что функция есть перегруженной. Общая форма объявления «перегруженной» функции с использованием ключевого слова overload имеет вид:

overload имя_функции;

Например. Пусть имеется перегруженная функция Max(), находящая максимум между множеством чисел, то строка

overload Max;

извещает компилятор о том, что функция Max() есть перегруженной.


Связанные темы