Полиморфизм в С++

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

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

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

Например, женщина может взять на себя множество ролей в разных ситуациях. Для ребенка она мать, домохозяйка в доме, работница в офисе и т.д. Таким образом, женщина берет на себя разные роли и ведет себя по-разному в разных условиях. Это реальный пример полиморфизма.

Точно так же и в мире программирования, оператор «+», который является оператором двоичного сложения, может вести себя по-разному при изменении операндов. Например, когда оба операнда являются числовыми, выполняется сложение.

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

Типы полиморфизма

Полиморфизм делится на два типа:

  • Полиморфизм времени компиляции
  • Полиморфизм времени выполнения

Диаграмма, представляющая типы полиморфизма, показана ниже:

Типы полиморфизма

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

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

Полиморфизм времени выполнения также известен как динамическое или позднее связывание или динамический полиморфизм.

Различия между полиморфизмом времени компиляции и полиморфизмом времени выполнения

Ниже мы рассмотрим основные различия между полиморфизмом временем компиляции и полиморфизмом времени выполнения:

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

Полиморфизм времени компиляции

Полиморфизм времени компиляции — это метод, при котором метод объекта вызывается во время компиляции.

Читать также:  Векторы в STL

Этот тип полиморфизма реализуется двумя способами:

  • Перегрузка функций
  • Перегрузка оператора

Мы подробно обсудим каждый способ ниже:

Перегрузка функций

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

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

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

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

Другие типы, такие как static и non-static, const и volatile и т.д., или объявления параметров, которые отличаются наличием или отсутствием значений по умолчанию, также не должны использоваться для перегрузки, поскольку они эквивалентны с точки зрения реализации.

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

Add(int,int);
Add(int,float);
Add(float,int);
Add(int,int,int);

В приведенных выше прототипах видно, что мы перегружаем функцию Add на основе типа параметров, последовательности или порядка параметров, количества параметров и т. д.

Давайте рассмотрим пример программирования, показывающий перегрузку функций:

Вывод данных:

35
191
19
Hello World

В приведенной выше программе есть класс Summation, в котором определены три перегруженные функции с именем Add, принимающие два целочисленных аргумента, три целочисленных аргумента и два строковых аргумента.

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

В этом случае сопоставляется функция int Add (int, int), так как внутренне число с плавающей запятой преобразуется в double, а затем сопоставляется с функцией с параметрами int. Если бы мы указали double вместо float, то у нас была бы другая перегруженная функция с параметрами double.

Последний вызов функции использует строковые значения в качестве параметров. В этом случае оператор добавления (+) действует как оператор конкатенации и объединяет два строковых значения для создания одной строки.

Преимущества перегрузки функций

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

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

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

Перегрузка оператора

Перегрузка операторов — это метод, с помощью которого мы придаем другое значение существующим операторам в C++. Другими словами, мы перегружаем операторы, чтобы придать особое значение определяемым пользователем типам данных как объектам.

Читать также:  Настройка среды разработки для C++

Большинство операторов в C++ перегружены или имеют специальное значение, чтобы они могли работать с пользовательскими типами данных. Обратите внимание, что при перегрузке основная работа операторов не изменяется. Перегрузка просто придает оператору дополнительное значение, сохраняя его основную семантику.

Хотя большинство операторов в C++ могут быть перегружены, некоторые операторы не могут быть перегружены.

Эти операторы перечислены ниже:

  • scope resolution operator(::) (оператор разрешения области видимости)
  • sizeof (измерение размера символов)
  • member selector(.) (селектор членов)
  • member pointer selector(*) (селектор указателя члена)
  • ternary operator(?:) (тернарный оператор)

Функции, которые мы используем для перегрузки операторов, называются «операторными функциями».

Операторные функции похожи на обычные функции, но с некоторыми отличиями. Отличие состоит в том, что имя операторной функции начинается с ключевого слова «operator», за которым следует символ оператора, который необходимо перегрузить.

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

Общий синтаксис операторной функции:

return_type classname::operator op(parameter list)
{
//function body
}

В синтаксисе «operator op» — это операторная функция, где operator — ключевое слово, а op — оператор, который нужно перегрузить. Return_type — тип возвращаемого значения.

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

Пример 1: Перегрузка унарного оператора с использованием функции оператора-члена:

Вывод данных:

Incremented Feet value: 10

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

Пример 2: Перегрузка бинарного оператора с помощью функции оператора-члена:

Вывод данных:

c1 = 2 + i5
c2 = 3 + i7
c3 = c1+c2 = 5 + i12

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

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

В приведенном ниже примере мы будем использовать дружественную функцию, чтобы добавить два комплексных числа, и увидеть разницу в реализации:

Вывод данных:

c1 = 2 + i5
c2 = 3 + i7
c3 = c1+c2 = 5 + i12

Мы можем видеть, что вывод программы такой же. Единственная разница в реализации — использование дружественной функции для перегрузки оператора + вместо функции-члена в предыдущей реализации.

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

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

Читать также:  Класс stringstream в C++

Пример 3: Перегрузка оператора с использованием оператора преобразования:

Вывод данных:

The resultant value of given fraction (3,5)= 0.6

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

В основной функции, когда мы присваиваем объект df переменной res_val, происходит преобразование, и результат сохраняется в res_val.

Мы также можем вызвать конструктор с одним аргументом. Когда мы вызываем конструктор из класса с помощью одного аргумента, это называется  конструктором преобразования». Конструктор преобразования можно использовать для неявного преобразования в создаваемый класс.

Вывод данных:

Point constructed using normal constructor
x = 20 y = 30
Point constructed using conversion constructor
x = 10 y = 0

В программе есть класс Point, который определяет конструктор со значениями по умолчанию. В основной функции мы строим объект pt с координатами x и y. Затем мы просто присваиваем pt значение 10. Здесь вызывается конструктор преобразования, и x присваивается значение 10, а y присваивается значение по умолчанию 0.

Правила перегрузки оператора

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

  • В C++ мы можем перегружать только существующие операторы. Новые добавленные операторы не могут быть перегружены.
  • Когда операторы перегружены, нам нужно убедиться, что хотя бы один из операндов имеет определенный пользователем тип.
  • Чтобы перегрузить определенные операторы, мы также можем использовать дружественную функцию.
  • Когда мы перегружаем унарные операторы с помощью функции-члена, она не принимает никаких явных аргументов.
  • Точно так же, когда бинарные операторы перегружаются с помощью функции-члена, мы должны предоставить функции один явный аргумент. Когда бинарные операторы перегружаются с помощью дружественной функции, функция принимает два аргумента.
  • В C++ есть два оператора, которые уже перегружены. Это «=» и «&». Поэтому, чтобы скопировать объект того же класса, нам не нужно перегружать оператор =, и мы можем использовать его напрямую.

Преимущества перегрузки оператора

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

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

Итог

Полиморфизм времени компиляции обеспечивает возможность перегрузки в основном для расширения функциональности кода с точки зрения перегрузки функций и перегрузки операторов.

Благодаря перегрузке функций мы можем написать более одной функции с одним и тем же именем, но с разными параметрами и типами. Это делает код простым и легко читаемым. Путем перегрузки операторов мы можем расширить функциональные возможности операторов, чтобы мы могли выполнять базовые операции и с пользовательскими типами.

В нашей следующей статье мы расскажем вам все о полиморфизме времени выполнения в C++.

С Уважением, МониторБанк

Добавить комментарий