НАСКОЛЬКО МНЕ ИЗВЕСТНО:
C ++ предоставляет три различных типа полиморфизма.
- Виртуальные функции
- Перегрузка имени функции
- Перегрузка оператора
В дополнение к трем вышеупомянутым типам полиморфизма существуют и другие виды полиморфизма:
- во время выполнения
- время компиляции
- специальный полиморфизм
- параметрический полиморфизм
Я знаю, что полиморфизм времени выполнения может быть достигнут с помощью виртуальных функций, а статический полиморфизм может быть достигнут с помощью шаблонных функций.
Но для двух других
специальный полиморфизм:
Если диапазон фактических типов, которые можно использовать, ограничен и комбинации должны быть индивидуально указаны перед использованием, это называется специальным полиморфизмом.
параметрический полиморфизм:
Если весь код написан без упоминания какого-либо конкретного типа и, таким образом, может прозрачно использоваться с любым количеством новых типов, это называется параметрическим полиморфизмом.
Я их с трудом понимаю :(
кто-нибудь может объяснить их оба, если возможно, на примере? Я надеюсь, что ответы на этот вопрос будут полезны для многих выпускников колледжей.
источник
Ответы:
Понимание / требования к полиморфизму
Чтобы понять полиморфизм - как этот термин используется в компьютерных науках - нужно начать с простого теста и определения его. Рассматривать:
Здесь
f()
нужно выполнить некоторую операцию и получить значенияx
и вy
качестве входных данных.C ++ механизмы полиморфизма
Явный полиморфизм, указанный программистом
Вы можете написать так
f()
, чтобы он мог работать с несколькими типами любым из следующих способов:Предварительная обработка:
Перегрузки:
Шаблоны:
Виртуальная рассылка:
Другие связанные механизмы
Предоставляемый компилятором полиморфизм для встроенных типов, стандартные преобразования и приведение / принуждение обсуждаются позже для полноты следующим образом:
терминология
Дальнейшая категоризация
Учитывая описанные выше полиморфные механизмы, мы можем классифицировать их по-разному:
Когда выбирается полиморфный код, зависящий от типа?
f
выше сint
аргументами - в зависимости от используемого полиморфного механизма и выбора встраивания компилятор может избежать генерации какого-либо кодаf(double)
, либо сгенерированный код может быть отброшен в какой-то момент компиляции или компоновки. ( все вышеперечисленные механизмы, кроме виртуальной отправки )Какие типы поддерживаются?
Параметрический смысл означает, что вы можете просто попробовать использовать функцию для различных типов параметров, не делая ничего специально, чтобы включить ее поддержку для них (например, шаблоны, макросы). Объект с функциями / операторами, которые действуют так, как ожидает шаблон / макрос 1, - это все, что шаблон / макрос должен выполнять свою работу, при этом точный тип не имеет значения. «Концепции», представленные C ++ 20, выражают и обеспечивают выполнение таких ожиданий - см. Страницу cppreference здесь .
Параметрический полиморфизм обеспечивает типирование утки - концепция, приписываемая Джеймсу Уиткомбу Райли, который, по-видимому, сказал: «Когда я вижу птицу, которая ходит, как утка, плавает, как утка, и крякает, как утка, я называю эту птицу уткой». ,
Полиморфизм подтипа (он же включение) позволяет работать с новыми типами без обновления алгоритма / функции, но они должны быть производными от того же базового класса (виртуальная диспетчеризация)
1 - Шаблоны очень гибкие. SFINAE (см. Также
std::enable_if
) эффективно допускает несколько наборов ожиданий для параметрического полиморфизма. Например, вы можете закодировать, что, когда тип данных, которые вы обрабатываете, имеет.size()
член, вы будете использовать одну функцию, в противном случае другая функция, которая не нужна.size()
(но, предположительно, каким-то образом страдает - например, при использовании более медленнойstrlen()
или не печатаемой как полезное сообщение в журнале). Вы также можете указать специальное поведение, когда шаблон создается с определенными параметрами, оставляя некоторые параметры параметрическими ( частичная специализация шаблона ) или нет ( полная специализация ).«Полиморфный»
Альф Штайнбах комментирует, что в стандарте C ++ полиморфизм относится только к полиморфизму времени выполнения с использованием виртуальной диспетчеризации. General Comp. Sci. значение более инклюзивное, согласно глоссарию создателя C ++ Бьярна Страуструпа ( http://www.stroustrup.com/glossary.html ):
Этот ответ - как и вопрос - связывает функции C ++ с Comp. Sci. терминология.
обсуждение
В стандарте C ++ используется более узкое определение «полиморфизма», чем в Comp. Sci. сообщества, чтобы обеспечить взаимопонимание для вашей аудитории, рассмотрите ...
Тем не менее, для того, чтобы стать отличным программистом на C ++, важно понимать, что на самом деле делает для вас полиморфизм ...
позволяя вам написать "алгоритмический" код один раз, а затем применить его ко многим типам данных
... а затем хорошо понимать, как различные полиморфные механизмы соответствуют вашим реальным потребностям.
Подходит для полиморфизма во время выполнения:
Base*
s,Когда нет четкого драйвера для полиморфизма времени выполнения, часто предпочтительнее использовать параметры времени компиляции. Рассматривать:
__FILE__
,__LINE__
, Строковый литерал конкатенации и другие уникальные возможности макросов (которые остаются злым ;-))Другие механизмы, поддерживающие полиморфизм
Как и было обещано, для полноты картины освещены несколько второстепенных тем:
Этот ответ завершается обсуждением того, как вышеперечисленное объединяется для расширения возможностей и упрощения полиморфного кода, особенно параметрического полиморфизма (шаблоны и макросы).
Механизмы отображения на операции, зависящие от типа
> Неявные перегрузки, предоставляемые компилятором
По сути, компилятор перегружает множество операторов для встроенных типов. Она концептуально не отличается от перегрузки, заданной пользователем, но указана, поскольку ее легко упустить. Например, вы можете добавить к
int
s иdouble
s, используя ту же нотацию,x += 2
и компилятор выдаст:Затем перегрузка легко распространяется на определяемые пользователем типы:
Предоставляемые компилятором перегрузки для базовых типов распространены в компьютерных языках высокого уровня (3GL +), и явное обсуждение полиморфизма обычно подразумевает нечто большее. (2GL - языки ассемблера - часто требуют, чтобы программист явно использовал разные мнемоники для разных типов.)
> Стандартные преобразования
Четвертый раздел стандарта C ++ описывает стандартные преобразования.
Первый пункт хорошо резюмируется (из старого черновика - надеюсь, все еще в основном правильного):
Ноль или одно преобразование из следующего набора: преобразование lvalue-to-rvalue, преобразование массива в указатель и преобразование функции в указатель.
Ноль или одно преобразование из следующего набора: интегральные продвижения, продвижение с плавающей запятой, интегральные преобразования, преобразования с плавающей запятой, преобразования с плавающей запятой, преобразования указателя, преобразования указателя в член и преобразования логического типа.
Ноль или одно преобразование квалификации.
Эти преобразования позволяют использовать такой код, как:
Применяя предыдущий тест:
a()
сам запускает код специально дляdouble
и поэтому не является полиморфным.Но, во втором вызове
a()
компилятор знает , для создания типа-соответствующий код для «раскрутки с плавающей точкой» (Standard § 4) , чтобы конвертировать42
в42.0
. Этот дополнительный код находится в вызывающей функции. Мы обсудим значение этого в заключении.> Принуждение, приведение, неявные конструкторы
Эти механизмы позволяют определяемым пользователем классам определять поведение, подобное стандартным преобразованиям встроенных типов. Давайте посмотрим:
Здесь объект
std::cin
оценивается в логическом контексте с помощью оператора преобразования. Это может быть концептуально сгруппировано с «интегральными рекламными предложениями» и др. Из стандартных преобразований в теме выше.Неявные конструкторы фактически делают то же самое, но управляются типом cast-to:
Последствия предоставленных компилятором перегрузок, преобразований и принуждения
Рассматривать:
Если мы хотим, чтобы сумма
x
обрабатывалась как действительное число во время деления (т.е. была 6,5, а не округлялась до 6), нам нужно всего лишь изменить ее наtypedef double Amount
.Это хорошо, но это не было бы слишком много работы , чтобы сделать код явно «введите правильный»:
Но учтите, что мы можем преобразовать первую версию в
template
:Это из - за эти маленькие «удобные функции» , что это может быть так легко экземплярами для любого
int
илиdouble
и работать , как задумано. Без этих функций нам потребовались бы явные приведения типов, черты типов и / или классы политик, некоторый подробный беспорядок, подверженный ошибкам, например:Таким образом, предоставляемая компилятором перегрузка операторов для встроенных типов, стандартные преобразования, приведение / принуждение / неявные конструкторы - все они вносят тонкую поддержку полиморфизма. Из определения в верхней части этого ответа они обращаются к «поиску и выполнению кода соответствующего типа» путем сопоставления:
"вдали" от типов параметров
из множества типов данных, которые обрабатывает полиморфный алгоритмический код
для кода, написанного для (потенциально меньшего) количества (того же или другого) типов.
"в" параметрические типы от значений постоянного типа
Они не устанавливают полиморфные контексты сами по себе, но помогают расширить возможности / упростить код внутри таких контекстов.
Вы можете почувствовать себя обманутым ... это не кажется таким уж большим. Значение состоит в том, что в параметрических полиморфных контекстах (т.е. внутри шаблонов или макросов) мы пытаемся поддерживать произвольно большой диапазон типов, но часто хотим выразить операции с ними в терминах других функций, литералов и операций, которые были разработаны для небольшой набор типов. Это снижает потребность в создании почти идентичных функций или данных для каждого типа, когда операция / значение логически одинаковы. Эти функции взаимодействуют, чтобы добавить позицию «максимальных усилий», делая то, что интуитивно ожидается, используя ограниченные доступные функции и данные и останавливаясь только с ошибкой, когда есть реальная двусмысленность.
Это помогает ограничить потребность в полиморфном коде, поддерживающем полиморфный код, сужая более жесткую сеть вокруг использования полиморфизма, чтобы локализованное использование не вызывало повсеместного использования, и делая преимущества полиморфизма доступными по мере необходимости без увеличения затрат на раскрытие реализации на во время компиляции, иметь несколько копий одной и той же логической функции в объектном коде для поддержки используемых типов и при выполнении виртуальной диспетчеризации в отличие от встраивания или, по крайней мере, разрешенных вызовов во время компиляции. Как обычно в C ++, программисту предоставляется большая свобода управления границами, в которых используется полиморфизм.
источник
dynamic_cast
требует «указателя на или lvalue полиморфного типа». Cheers & hth.,В C ++ важное различие заключается в привязке во время выполнения и во время компиляции. Ad-hoc или параметрический не очень помогают, как я объясню позже.
Примечание. Полиморфизм времени выполнения может быть разрешен во время компиляции, но это всего лишь оптимизация. Необходимость в эффективной поддержке разрешения во время выполнения и компромисс с другими проблемами является частью того, что привело к тому, что виртуальные функции стали тем, чем они являются. И это действительно ключ ко всем формам полиморфизма в C ++ - каждая возникает из разных наборов компромиссов, сделанных в разном контексте.
Перегрузка функций и перегрузка операторов - это одно и то же во всех смыслах. Имена и синтаксис их использования не влияют на полиморфизм.
Шаблоны позволяют одновременно указывать множество перегрузок функций.
Есть еще один набор названий той же идеи времени разрешения ...
Эти имена больше связаны с ООП, поэтому немного странно сказать, что шаблон или другая функция, не являющаяся членом, использует раннее связывание.
Чтобы лучше понять взаимосвязь между виртуальными функциями и перегрузкой функций, также полезно понять разницу между «одиночной отправкой» и «множественной отправкой». Идею можно понять как прогрессию ...
Очевидно, что ООП - это нечто большее, чем оправдание для обозначения одного параметра как особого, но это одна его часть. Возвращаясь к тому, что я сказал о компромиссах - однократную отправку довольно легко сделать эффективно (обычная реализация называется «виртуальными таблицами»). Множественная отправка более неудобна не только с точки зрения эффективности, но и для раздельной компиляции. Если вам интересно, вы можете найти «проблема выражения».
Так же, как немного странно использовать термин «раннее связывание» для функций, не являющихся членами, немного странно использовать термины «единичная отправка» и «множественная отправка», где полиморфизм разрешается во время компиляции. Обычно считается, что C ++ не имеет множественной отправки, что считается особым видом разрешения времени выполнения. Однако перегрузку функции можно рассматривать как множественную отправку, выполняемую во время компиляции.
Возвращаясь к параметрическому и произвольному полиморфизму, эти термины более популярны в функциональном программировании и не совсем работают в C ++. Несмотря на это...
Параметрический полиморфизм означает, что у вас есть типы в качестве параметров, и используется один и тот же код независимо от того, какой тип вы используете для этих параметров.
Специальный полиморфизм является ситуативным в том смысле, что вы предоставляете разный код в зависимости от конкретных типов.
Перегрузка и виртуальные функции являются примерами произвольного полиморфизма.
Опять же, есть синонимы ...
За исключением того, что это не совсем синонимы, хотя обычно к ним относятся так, как если бы они были, и именно здесь в C ++ может возникнуть путаница.
Причина, по которой они рассматриваются как синонимы, состоит в том, что, ограничивая полиморфизм определенными классами типов, становится возможным использовать операции, специфичные для этих классов типов. Слово «классы» здесь можно интерпретировать в смысле ООП, но на самом деле оно просто относится к (обычно именованным) наборам типов, которые совместно используют определенные операции.
Таким образом, параметрический полиморфизм обычно понимается (по крайней мере, по умолчанию) как подразумевающий неограниченный полиморфизм. Поскольку один и тот же код используется независимо от параметров типа, единственными поддерживаемыми операциями являются те, которые работают для всех типов. Оставляя набор типов неограниченным, вы резко ограничиваете набор операций, которые можно применить к этим типам.
Например, в Haskell вы можете ...
a
Здесь является непринужденным полиморфным типом. Это может быть что угодно, поэтому мы мало что можем сделать со значениями этого типа.Здесь
a
он должен быть членомNum
класса - типы, которые действуют как числа. Это ограничение позволяет вам делать числовые вещи с этими значениями, например добавлять их. Даже3
вывод полиморфного типа показывает, что вы имеете в виду3
of typea
.Я думаю об этом как об ограниченном параметрическом полиморфизме. Есть только одна реализация, но ее можно применять только в ограниченных случаях. Специальный аспект - это выбор того, что
+
и3
использовать. Каждый «экземпляр»Num
имеет свою собственную реализацию. Так что даже в Haskell «параметрический» и «неограниченный» на самом деле не синонимы - не вините меня, это не моя вина!В C ++ и перегрузка, и виртуальные функции представляют собой произвольный полиморфизм. Определению специального полиморфизма не важно, выбрана ли реализация во время выполнения или во время компиляции.
C ++ очень близко подходит к параметрическому полиморфизму с шаблонами, если каждый параметр шаблона имеет тип
typename
. Есть параметры типа и одна реализация, независимо от того, какие типы используются. Однако правило «Ошибка замены не является ошибкой» означает, что неявные ограничения возникают в результате использования операций в шаблоне. Дополнительные сложности включают специализацию шаблона для предоставления альтернативных шаблонов - различных (специальных) реализаций.Таким образом, в некотором смысле C ++ имеет параметрический полиморфизм, но он неявно ограничен и может быть отменен специальными альтернативами - т.е. эта классификация на самом деле не работает для C ++.
источник
a
здесь неограниченный полиморфный тип [...], поэтому мы мало что можем сделать со значениями этого типа». представлял интерес - в C ++ без концепций вы не ограничены только попыткой выполнения определенного набора операций с аргументом типа, указанного в качестве параметра шаблона ... библиотеки, такие как концепции повышения, работают иначе - убедитесь, что тип поддерживает операции вы указываете, а не предохраняетесь от случайного использования дополнительных операций.Что касается специального полиморфизма, то это означает перегрузку функции или перегрузки оператора. Посмотрите здесь:
http://en.wikipedia.org/wiki/Ad-hoc_polymorphism
Что касается параметрического полиморфизма, шаблонные функции также могут учитываться, потому что они не обязательно принимают параметры ФИКСИРОВАННЫХ типов. Например, одна функция может сортировать массив целых чисел, а также массив строк и т. Д.
http://en.wikipedia.org/wiki/Parametric_polymorphism
источник
<
и подобных операторов). В Haskell вы бы явно выразили это требование с помощью классаOrd
. Тот факт, что вы получаете разные значения в<
зависимости от конкретного типа (как указано в экземпляреOrd
), будет рассматриваться как специальный полиморфизм.Возможно, это не поможет, но я сделал это, чтобы познакомить своих друзей с программированием, выдав определенные функции, например
START
, иEND
для основной функции, чтобы это не было слишком сложной задачей (они использовали только файл main.cpp ). Он содержит полиморфные классы и структуры, шаблоны, векторы, массивы, директивы препроцессора, дружбу, операторы и указатели (все из которых вы, вероятно, должны знать, прежде чем пытаться полиморфизм):Примечание: это не закончено, но вы можете понять
main.cpp
main.h
источник
Вот базовый пример использования полиморфных классов
источник
Полиморфизм означает, что многие формы как таковые используются для того, чтобы оператор действовал по-разному в разных случаях. Полиморфизм используется для реализации наследования. Например, мы определили fn draw () для формы класса, после чего функция draw может быть реализована для рисования круга, прямоугольника, треугольника и других фигур. (которые являются объектами формы класса)
источник
Если кто-нибудь скажет ОТРЕЗАТЬ этим людям
Что случится?
Итак, приведенное выше представление показывает, что такое полиморфизм (то же имя, разное поведение) в ООП.
Если вы идете на собеседование, и интервьюер просит вас рассказать / показать живой пример полиморфизма в той же комнате, где мы сидим, скажем:
Ответ - Дверь / Окна
Хотите знать, как?
Через дверь / окно - может прийти человек, может прийти воздух, может прийти свет, может пойти дождь и т. Д.
т.е. одна форма другого поведения (полиморфизм).
Чтобы понять это лучше и проще, я использовал пример выше. Если вам нужна ссылка на код, следуйте приведенным выше ответам.
источник