Полиморфизм. Перегрузка функций

Слово полиморфизм греческого происхождения: "наличие в пределах од­ного вида резко отличающихся по облику особей". В языке С каждая функция должна иметь уникальное имя. Одним из путей реализации полиморфизма в С++ является перегрузка функций (overloading), то есть использование одного име­ни для разных функций. Компилятор различает такие функции по числу и типу параметров. В С++ могут перегружаться любые функции, в том числе и методы класса. При этом действуют следующие ограничения.

1. Не могут перегружаться функции, имеющие совпадающие тип и число аргументов, но разные типы возвращаемых значений. Например, если объявить функции: int fun(long i) и long fun(long i), то при вызове функции: fun(70000) перед компилятором встанет неразрешимая пробле­ма выбора, так как при вызове функции тип возвращаемого значения ни­как не может быть указан.

2. Не могут перегружаться функции, имеющие неявно совпадающие типы аргументов, например, int и int&.

Пример 13.

Рассмотрим механизмы описания и использования перегружаемых функ­ций получения суммы двух аргументов.

#include<iostream.h>

// перегрузка функции add:

int add(int i, int j)

{ cout<<"Сложение целых чисел\n";

return i + j;

}

double add(double i, double j)

{ cout<<"Сложение вещественных чисел\n";

return i + j;

}

void main ()

{ cout << "2 + 3=" << add(2,3) << '\n';

cout << "2.5 + 2.0=" << add(2.5, 2.0) << '\n';

}

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

Перегрузка конструкторов

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

Пример 14.

Демонстрация перегруженных конструкторов.

class X

{ int integer_part;

double double_part;

public:

X ( int i ) { integer_part = i; } // конструктор с инициализацией

X ( double d) // перегруженный конструктор

{ double_part = d; }

};

void main()

{ X one (10); // вызов конструктора X::X(int)

X two (3.14); // вызов конструктора X::X(double)

}

Функция с параметрами по умолчанию

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

Например:

char func (char ch, int i, int k=5)

{ тело функции }

В функции func при ссылке на нее с тремя аргументами, например, при вызове функции:

ret = func ('A', 10, 25);

параметры получат значения: ch = 'A', i = 10, k = 25.

Но если при вызове функции список аргументов будет состоять из двух элементов, например:

ret = func ('A', 10);

то параметры получат значения: ch = 'A', i = 10, k = 5 (по умолчанию).

Если функция описана, например, так:

char Fun (int a = 5, int j = 7)

{ тело функции },

то она может вызываться с двумя или одним аргументом или даже вовсе без аргументов.

Конструктор по умолчанию

Конструктор, не имеющий параметров, называется конструктором по умолчанию. Если отсутствует определенный пользователем конструктор по умолчанию, компилятор С++ генерирует собственный конструктор по умолча­нию для описанного класса – это конструктор, например, класса Х, который не принимает никаких аргументов X :: X().

Конструктор, как и любая функция, может иметь любое количество пара­метров, в том числе и параметры по умолчанию. Например, конструктор

X :: X (int, int=0)

может принимать один или два аргумента. Если будет только один аргумент, то недостающий второй аргумент будет принят как 0. Аналогично, конструктор

X :: X (int=5, int=6)

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

Однако конструктор по умолчанию X :: X() не принимает аргументов во­обще, и его не следует путать с конструктором, например, X :: X (int=0), кото­рый может либо принимать один аргумент, либо не принимать аргументов. При вызове конструкторов следует избегать неоднозначностей. В следующем приме­ре возможна неоднозначная трактовка компилятором конструктора по умолча­нию и конструктора, принимающего целый параметр.

Пример 15.

Неоднозначность конструкторов.

class X

{ public:

X();

X( int = 0);

};

void main()

{ X one (10); // так МОЖНО: используется конструктор X::X(int)

X two; // так НЕЛЬЗЯ: X::X() или X::X(int=0) ?

}

Конструктор копирования

Это особый тип конструктора. Конструктор копирования для класса Х – это конструктор, который имеет один аргумент ссылочного типа X& и, возмож­но, параметры по умолчанию. Ссылочный тип тесно связан с типом указатель (X*) и позволяет передавать в функцию не сам объект, а только ссылку на него.

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

Например, для класса X корректное описание конструктора копирования может быть таким:

class X

{ public:

X() {...} // конструктор по умолчанию

X(const X&) {...} // конструктор копирования

X(const X&, int i=4) {...} // конструктор копирования с параметром // по умолчанию

};

Конструктор копирования вызывается тогда, когда выполняется копирова­ние объекта, обычно при объявлении объекта с его инициализацией, например:

X one; // вызов конструктора по умолчанию

X two = one; // вызов конструктора копирования

X two (one); // вызов конструктора копирования с параметром

По умолчанию, копия объекта класса содержит копию каждого члена, т.е. объект two будет полной копией объекта one. Аналогично, объекты класса мо­гут по умолчанию почленно копироваться при помощи операции присваивания:

X three;

three = one;

Подобное почленное копирование приемлемо, если оно выполняется для статических и автоматических переменных класса. Выделение динамической памяти для объектов с использованием указателей и их копирование будет рассмотрено ниже.

Наши рекомендации