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

НАСКОЛЬКО МНЕ ИЗВЕСТНО:

C++ предоставляет три различных типа полиморфизма.

  • Виртуальные функции
  • Перегрузка имени функции
  • Перегрузка оператора

В дополнение к вышеупомянутым трем типам полиморфизма существуют и другие виды полиморфизма:

  • время выполнения
  • время компиляции
  • специальный полиморфизм
  • параметрический полиморфизм

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

Но для двух других

специальный полиморфизм:

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

параметрический полиморфизм:

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

Я едва могу понять их :(

Может ли кто-нибудь объяснить им обоим, если это возможно, на примере? Я надеюсь, что ответы на эти вопросы будут полезны для многих новых отрывков из их колледжей.

Ответы

Ответ 1

Понимание/требований к полиморфизму

Чтобы понять полиморфизм - поскольку этот термин используется в Computing Science, он помогает начать с простого теста и определения его. Рассмотрим:

    Type1 x;
    Type2 y;

    f(x);
    f(y);

Здесь f() должен выполнить некоторую операцию и в качестве входов вводить значения x и y.

Чтобы проявить полиморфизм, f() должен иметь возможность работать со значениями по меньшей мере двух различных типов (например, int и double), находить и выполнять отдельный код, соответствующий типу.


Механизмы С++ для полиморфизма

Явный полиморфизм, указанный программистом

Вы можете написать f(), чтобы он мог работать с несколькими типами одним из следующих способов:

  • Препроцессирование:

    #define f(X) ((X) += 2)
    // (note: in real code, use a longer uppercase name for a macro!)
    
  • Перегрузки:

    void f(int& x)    { x += 2; }
    
    void f(double& x) { x += 2; }
    
  • Шаблоны:

    template <typename T>
    void f(T& x) { x += 2; }
    
  • Виртуальная отправка:

    struct Base { virtual Base& operator+=(int) = 0; };
    
    struct X : Base
    {
        X(int n) : n_(n) { }
        X& operator+=(int n) { n_ += n; return *this; }
        int n_;
    };
    
    struct Y : Base
    {
        Y(double n) : n_(n) { }
        Y& operator+=(int n) { n_ += n; return *this; }
        double n_;
    };
    
    void f(Base& x) { x += 2; } // run-time polymorphic dispatch
    

Другие связанные механизмы

Полиморфизм, предоставляемый компилятором для встроенных типов, стандартные преобразования и литье/принуждение, обсуждается позже для полноты:

  • они обычно интуитивно понятны (гарантируя реакцию "о, эта" ),
  • они влияют на пороговое значение в требовании, а также на бесшовность при использовании вышеуказанных механизмов и
  • объяснение - это отвлекающее внимание от более важных концепций.

Терминология

Дальнейшая классификация

Учитывая описанные выше полиморфные механизмы, мы можем классифицировать их по-разному:

  • Когда выбран код, специфичный для полиморфного типа?

    • Время выполнения означает, что компилятор должен генерировать код для всех типов, которые программа могла бы обрабатывать во время работы, а во время выполнения выбирается правильный код (виртуальная отправка)
    • Время компиляции означает, что во время компиляции выполняется выбор кода, специфичного для конкретного типа. Вследствие этого: скажем, что программа, называемая только f выше с аргументами int - в зависимости от используемого полиморфного механизма и выбора вариантов, компилятор может избежать генерации какого-либо кода для f(double), или сгенерированный код может быть выброшен на некоторых точка в компиляции или связывании. (все механизмы выше, кроме виртуальной отправки)

  • Какие типы поддерживаются?

    • Ad-hoc означает, что вы предоставляете явный код для поддержки каждого типа (например, перегрузка, специализация шаблона); вы явно добавляете поддержку "для этого" (как указано в специальном смысле), некоторые другие "this" и, возможно, "это" тоже; -).
    • Параметрический означает, что вы можете просто попробовать использовать эту функцию для различных типов параметров, не делая специально что-либо, чтобы включить их поддержку для них (например, шаблоны, макросы). Объект с функциями/операторами, которые действуют как шаблон/макрос, ожидает, что 1 - это все, что шаблон/макрос должен выполнять свою работу, причем точный тип не имеет значения. "Концепции", вырезанные из С++ 11, помогают выражать и оправдывать такие ожидания - пусть надеется, что они перейдут в более поздний стандарт.

      • Параметрический полиморфизм обеспечивает утиную типизацию - концепцию, приписываемую Джеймсу Уиткомбу Райли, который, по-видимому, сказал: "Когда я вижу птицу, которая ходит как утка и плавает, как утка и шарлатанья, как утка, Я называю эту птицу утки".

        template <typename Duck>
        void do_ducky_stuff(const Duck& x) { x.walk().swim().quack(); }
        
        do_ducky_stuff(Vilified_Cygnet());
        
    • Политизм подтипа (или включение) позволяет вам работать с новыми типами без обновления алгоритма/функции, но они должны быть получены из одного и того же базового класса (виртуальная диспетчеризация)

1 - Шаблоны чрезвычайно гибкие. SFINAE (см. также std::enable_if) эффективно позволяет несколько наборов ожиданий для параметрического полиморфизма. Например, вы можете кодировать, что, когда тип данных, которые вы обрабатываете, имеет член .size(), вы будете использовать одну функцию, в противном случае - другую функцию, которая не нуждается в .size() (но, по-видимому, каким-то образом страдает - например, используя медленнее strlen() или не печатать как полезное сообщение в журнале). Вы также можете указать ad-hoc поведение, когда шаблон создается с помощью определенных параметров, либо оставляя некоторые параметры параметрическими (специализация частичного шаблона) или нет (полная специализация).

"полиморфный"

Альф Штайнбах комментирует, что в С++ Standard полиморфноотносится только к полиморфизму во время выполнения с использованием виртуальной диспетчеризации. Общий комп. Sci. значение является более инклюзивным, согласно глоссарию разработчика С++ Creator Bjarne Stroustrup (http://www.stroustrup.com/glossary.html):

полиморфизм - предоставление единого интерфейса для объектов разных типов. Виртуальные функции обеспечивают динамический (run-time) полиморфизм через интерфейс, предоставляемый базовым классом. Перегруженные функции и шаблоны обеспечивают статический (компиляционный) полиморфизм. TС++ PL 12.2.6, 13.6.1, D & E 2.9.

Этот ответ - как и вопрос - связывает функции С++ с Comp. Sci. терминология.

Обсуждение

С С++ Standard, используя более узкое определение "полиморфизма", чем Comp. Sci. сообщества, чтобы обеспечить взаимопонимание для вашей аудитории...

  • с использованием однозначной терминологии ( "можем ли мы сделать этот код повторно используемым для других типов?" или "можно ли использовать виртуальную отправку?", а не "можем ли мы сделать этот код полиморфным?" ) и/или
  • четко определяет вашу терминологию.

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

    позволить вам написать "алгоритмический" код один раз, а затем применить его ко многим типам данных

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

Коэффициент полиморфизма времени выполнения:

  • обработанный методами factory и выплющенный в виде гетерогенной коллекции объектов, обработанной через Base* s,
  • выбранная во время выполнения на основе файлов конфигурации, переключателей командной строки, настроек пользовательского интерфейса и т.д., Реализация
  • различна во время выполнения, например, для шаблона конечного автомата.

Когда нет четкого драйвера для полиморфизма во время выполнения, часто предпочтительны параметры времени компиляции. Рассмотрим:

  • компиляция - то, что называется аспектом шаблонных классов, предпочтительнее, если интерфейсы жира не работают во время выполнения.
  • SFINAE
  • CRTP Оптимизация
  • (многие из них включают в себя вставку и удаление мертвого кода, разворот цикла, статические массивы на основе стека против кучи)
  • __FILE__, __LINE__, конкатенация строковых литералов и другие уникальные возможности макросов (которые остаются злыми; -))
  • Поддерживается семантическое использование шаблонов и макросов, но не искусственно ограничивает способ предоставления этой поддержки (поскольку виртуальная диспетчеризация имеет тенденцию требовать точно совпадения функций-членов)

Другие механизмы, поддерживающие полиморфизм

Как и было обещано, для полноты охвачены несколько периферийных тем:

  • перегруженные компилятором
  • преобразования
  • проливает/принуждение

Этот ответ завершается обсуждением того, как вышеупомянутое объединяет возможности расширения и упрощения полиморфного кода - особенно параметрический полиморфизм (шаблоны и макросы).

Механизмы для сопоставления конкретным операциям

Неявные компиляционные перегрузки

Концептуально компилятор перегружает многие операторы для встроенных типов. Он не концептуально отличается от пользовательской перегрузки, но указан как легко упускается из виду. Например, вы можете добавить к int и double, используя ту же нотацию x += 2, и компилятор производит:

  • инструкции по процессору типа
  • результат одного и того же типа.

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

std::string x;
int y = 0;

x += 'c';
y += 'c';

Перегруженные компилятором основные типы распространены на языках высокого уровня (3GL +), и явное обсуждение полиморфизма обычно подразумевает нечто большее. (2GLs - языки ассемблера - часто требуется, чтобы программист явно использовал разные мнемоники для разных типов.)

> Стандартные преобразования

Четвертый раздел стандарта С++ описывает стандартные преобразования.

Первая точка прекрасно суммируется (из старого проекта, мы надеемся, по-прежнему в значительной степени исправляем):

-1- Стандартные преобразования - это неявные преобразования, определенные для встроенных типов. Параметр conv перечисляет полный набор таких преобразований. Стандартная последовательность преобразования представляет собой последовательность стандартных преобразований в следующем порядке:

  • Нулевое или одно преобразование из следующего набора: преобразование lvalue-в-rval, преобразование матрицы в указатель и преобразование функции в указатель.

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

  • Нулевое или одно квалификационное преобразование.

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

Эти преобразования позволяют использовать код, например:

double a(double x) { return x + 2; }

a(3.14);
a(42);

Применение предыдущего теста:

Чтобы быть полиморфным, [ a()] должен иметь возможность работать со значениями как минимум двух различных типов (например, int и double), поиск и выполнение типа подходящий код.

a() сам запускает код специально для double и поэтому не является полиморфным.

Но во втором вызове a() компилятор знает, чтобы сгенерировать соответствующий типу код для "продвижения по плавающей запятой" (стандарт §4) для преобразования 42 в 42.0. Этот дополнительный код находится в вызывающей функции. Мы обсудим значение этого в заключении.

Принуждение, приведения, неявные конструкторы

Эти механизмы позволяют пользовательским классам определять поведение, сходное со встроенными типами "Стандартные преобразования". Давайте посмотрим:

int a, b;

if (std::cin >> a >> b)
    f(a, b);

Здесь объект std::cin оценивается в булевом контексте с помощью оператора преобразования. Это может быть концептуально сгруппировано с "интегральными продвижениями" и др. Из Стандартных преобразований в вышеприведенной теме.

Неявные конструкторы эффективно выполняют одно и то же, но контролируются типом cast-to:

f(const std::string& x);
f("hello");  // invokes `std::string::string(const char*)`

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

Рассмотрим:

void f()
{
    typedef int Amount;
    Amount x = 13;
    x /= 2;
    std::cout << x * 1.1;
}

Если мы хотим, чтобы количество x рассматривалось как действительное число во время деления (то есть должно быть 6.5, а не округлено до 6), нам нужно изменить только typedef double Amount.

Это хорошо, но это не слишком много делало, чтобы код явно "правильно набирал":

void f()                               void f()
{                                      {
    typedef int Amount;                    typedef double Amount;
    Amount x = 13;                         Amount x = 13.0;
    x /= 2;                                x /= 2.0;
    std::cout << double(x) * 1.1;          std::cout << x * 1.1;
}                                      }

Но рассмотрим, что мы можем преобразовать первую версию в template:

template <typename Amount>
void f()
{
    Amount x = 13;
    x /= 2;
    std::cout << x * 1.1;
}

Из-за этих небольших "удобных функций" он может быть так легко создан для int или double и работать по назначению. Без этих функций нам нужны явные приведения, черты типов и/или классы политик, некоторые подробные, подверженные ошибкам беспорядки вроде:

template <typename Amount, typename Policy>
void f()
{
    Amount x = Policy::thirteen;
    x /= static_cast<Amount>(2);
    std::cout << traits<Amount>::to_double(x) * 1.1;
}

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

  • "прочь" из типов параметров

    • из многих типов данных обрабатываются полиморфные алгоритмические коды кода

    • для кода, написанного для (потенциально меньшего) числа (тех же или других) типов.

  • "to" параметрические типы из значений константного типа

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

Вы можете чувствовать себя обманутым... это не похоже на много. Значимость заключается в том, что в параметрических полиморфных контекстах (т.е. Внутри шаблонов или макросов) мы пытаемся поддерживать произвольно большой диапазон типов, но часто хотим выражать на них операции с точки зрения других функций, литералов и операций, которые были разработаны для небольшой набор типов. Это уменьшает необходимость создания почти идентичных функций или данных для каждого типа, когда операция/значение логически одинаковы. Эти функции взаимодействуют, чтобы добавить отношение "лучших усилий", делая то, что интуитивно ожидалось, используя ограниченные доступные функции и данные и только останавливаясь с ошибкой, когда возникает реальная двусмысленность.

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

Ответ 2

В С++ важным отличием является время выполнения и привязка времени компиляции. Ad-hoc против параметрического не помогает, как я объясню позже.

|----------------------+--------------|
| Form                 | Resolved at  |
|----------------------+--------------|
| function overloading | compile-time |
| operator overloading | compile-time |
| templates            | compile-time |
| virtual methods      | run-time     |
|----------------------+--------------|

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

Перегрузка функций и перегрузка оператора - это то же самое во всех отношениях. Имена и синтаксис их использования не влияют на полиморфизм.

Шаблоны позволяют сразу указывать множество перегрузок функций.

Существует еще один набор имен для той же идеи с разрешением времени...

|---------------+--------------|
| early binding | compile-time |
| late binding  | run-time     |
|---------------+--------------|

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

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

  • Во-первых, существуют мономорфные функции. Реализация функции уникально идентифицируется именем функции. Ни один из параметров не является специальным.
  • Тогда есть отдельная отправка. Один из параметров считается специальным и используется (вместе с именем), чтобы определить, какую реализацию использовать. В ООП мы склонны рассматривать этот параметр как "объект", перечислить его перед именем функции и т.д.
  • Затем, существует многократная отправка. Любые/все параметры способствуют определению того, какую реализацию использовать. Поэтому, опять же, ни один из параметров не должен быть особенным.

В ООП, очевидно, больше, чем предлог для назначения одного параметра как специального, но это его часть. И в связи с тем, что я сказал о компромиссах - единая диспетчеризация достаточно проста для эффективного выполнения (обычная реализация называется "виртуальными таблицами" ). Многократная отправка более неудобна, не только с точки зрения эффективности, но и для отдельной компиляции. Если вам интересно, вы можете найти "проблему с выражением".

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

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

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

Ad-hoc-полиморфизм является ad-hoc в том смысле, что вы предоставляете другой код в зависимости от конкретных типов.

Перегрузка и виртуальные функции являются примерами ad-hoc-полиморфизма.

Опять же, есть некоторые синонимы...

|------------+---------------|
| parametric | unconstrained |
| ad-hoc     | constrained   |
|------------+---------------|

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

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

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

В частности. Haskell, вы можете...

myfunc1 :: Bool -> a -> a -> a
myfunc1 c x y = if c then x else y

a здесь - неограниченный полиморфный тип. Это может быть что угодно, поэтому мы не можем много сделать со значениями этого типа.

myfunc2 :: Num a => a -> a
myfunc2 x = x + 3

Здесь a ограничивается членом классов Num, которые действуют как числа. Это ограничение позволяет вам делать числовые вещи с этими значениями, например добавлять их. Даже 3 являются выводами типа полиморфного типа, что вы имеете в виду 3 типа a.

Я рассматриваю это как ограниченный параметрический полиморфизм. Там только одна реализация, но ее можно применять только в ограниченных случаях. Абонентский аспект - это выбор для использования + и 3. Каждый "экземпляр" Num имеет свою собственную реализацию. Поэтому даже в Haskell "параметрические" и "безусловные" на самом деле не синонимы - не обвиняйте меня, это не моя вина!

В С++ обе перегрузочные и виртуальные функции являются ad-hoc-полиморфизмом. Определение ad-hoc-полиморфизма не имеет значения, выбрана ли реализация во время выполнения или время компиляции.

С++ очень близок к параметрическому полиморфизму с шаблонами, если каждый параметр шаблона имеет тип typename. Существуют параметры типа, и есть одна реализация, независимо от того, какие типы используются. Однако правило "Ошибка замены не является ошибкой" означает, что неявные ограничения возникают в результате использования операций внутри шаблона. Дополнительные сложности включают специализированную специализацию для предоставления альтернативных шаблонов - разные (ad-hoc) реализации.

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

Ответ 3

Что касается ad-hoc-полиморфизма, это означает перегрузку функций или перегрузку оператора. Проверьте здесь:

http://en.wikipedia.org/wiki/Ad-hoc_polymorphism

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

http://en.wikipedia.org/wiki/Parametric_polymorphism

Ответ 4

Это может не помочь, но я сделал это, чтобы представить моих друзей программированию, выделив определенные функции, такие как START и END для основной функции, поэтому он не был слишком сложным (они использовали только файл main.cpp). Он содержит полиморфные классы и структуры, шаблоны, векторы, массивы, предпроцессорные директивы, дружбу, операторы и указатели (все, что вы, вероятно, должны знать перед попыткой полиморфизма):

Примечание. Это еще не закончено, но вы можете получить идею

main.cpp

#include "main.h"
#define ON_ERROR_CLEAR_SCREEN false
START
    Library MyLibrary;
    Book MyBook("My Book", "Me");
    MyBook.Summarize();
    MyBook += "Hello World";
    MyBook += "HI";
    MyBook.EditAuthor("Joe");
    MyBook.EditName("Hello Book");
    MyBook.Summarize();
    FixedBookCollection<FairyTale> FBooks("Fairytale Books");
    FairyTale MyTale("Tale", "Joe");
    FBooks += MyTale;
    BookCollection E("E");
    MyLibrary += E;
    MyLibrary += FBooks;
    MyLibrary.Summarize();
    MyLibrary -= FBooks;
    MyLibrary.Summarize();
    FixedSizeBookCollection<5> Collection("My Fixed Size Collection");
    /* Extension Work */ Book* Duplicate = MyLibrary.DuplicateBook(&MyBook);
    /* Extension Work */ Duplicate->Summarize();
END

main.h

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <type_traits>
#include <array>
#ifndef __cplusplus
#error Not C++
#endif
#define START int main(void)try{
#define END GET_ENTER_EXIT return(0);}catch(const std::exception& e){if(ON_ERROR_CLEAR_SCREEN){system("cls");}std::cerr << "Error: " << e.what() << std::endl; GET_ENTER_EXIT return (1);}
#define GET_ENTER_EXIT std::cout << "Press enter to exit" << std::endl; getchar();
class Book;
class Library;
typedef std::vector<const Book*> Books;
bool sContains(const std::string s, const char c){
    return (s.find(c) != std::string::npos);
}
bool approve(std::string s){
    return (!sContains(s, '#') && !sContains(s, '%') && !sContains(s, '~'));
}
template <class C> bool isBook(){
    return (typeid(C) == typeid(Book) || std::is_base_of<Book, C>());
}
template<class ClassToDuplicate> class DuplicatableClass{ 
public:
    ClassToDuplicate* Duplicate(ClassToDuplicate ToDuplicate){
        return new ClassToDuplicate(ToDuplicate);
    }
};
class Book : private DuplicatableClass<Book>{
friend class Library;
friend struct BookCollection;
public:
    Book(const char* Name, const char* Author) : name_(Name), author_(Author){}
    void operator+=(const char* Page){
        pages_.push_back(Page);
    }
    void EditAuthor(const char* AuthorName){
        if(approve(AuthorName)){
            author_ = AuthorName;
        }
        else{
            std::ostringstream errorMessage;
            errorMessage << "The author of the book " << name_ << " could not be changed as it was not approved";
            throw std::exception(errorMessage.str().c_str());
        }
    }
    void EditName(const char* Name){
        if(approve(Name)){
            name_ = Name;
        }
        else{
            std::ostringstream errorMessage;
            errorMessage << "The name of the book " << name_ << " could not be changed as it was not approved";
            throw std::exception(errorMessage.str().c_str());
        }
    }
    virtual void Summarize(){
        std::cout << "Book called " << name_ << "; written by " << author_ << ". Contains "
            << pages_.size() << ((pages_.size() == 1) ? " page:" : ((pages_.size() > 0) ? " pages:" : " pages")) << std::endl;
        if(pages_.size() > 0){
            ListPages(std::cout);
        }
    }
private:
    std::vector<const char*> pages_;
    const char* name_;
    const char* author_;
    void ListPages(std::ostream& output){
        for(int i = 0; i < pages_.size(); ++i){
            output << pages_[i] << std::endl;
        }
    }
};
class FairyTale : public Book{
public:
    FairyTale(const char* Name, const char* Author) : Book(Name, Author){}
};
struct BookCollection{
friend class Library;
    BookCollection(const char* Name) : name_(Name){}
    virtual void operator+=(const Book& Book)try{
        Collection.push_back(&Book); 
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
    virtual void operator-=(const Book& Book){
        for(int i = 0; i < Collection.size(); ++i){
            if(Collection[i] == &Book){
                Collection.erase(Collection.begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
private:
    const char* name_;
    Books Collection;
};
template<class FixedType> struct FixedBookCollection : public BookCollection{
    FixedBookCollection(const char* Name) : BookCollection(Name){
        if(!isBook<FixedType>()){
            std::ostringstream errorMessage;
            errorMessage << "The type " << typeid(FixedType).name() << " cannot be initialized as a FixedBookCollection";
            throw std::exception(errorMessage.str().c_str());
            delete this;
        }
    }
    void operator+=(const FixedType& Book)try{
        Collection.push_back(&Book); 
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
    void operator-=(const FixedType& Book){
        for(int i = 0; i < Collection.size(); ++i){
            if(Collection[i] == &Book){
                Collection.erase(Collection.begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
private:
    std::vector<const FixedType*> Collection;
};
template<size_t Size> struct FixedSizeBookCollection : private std::array<const Book*, Size>{
    FixedSizeBookCollection(const char* Name) : name_(Name){ if(Size < 1){ throw std::exception("A fixed size book collection cannot be smaller than 1"); currentPos = 0; } }
    void operator+=(const Book& Book)try{
        if(currentPos + 1 > Size){
            std::ostringstream errorMessage;
            errorMessage << "The FixedSizeBookCollection " << name_ << " size capacity has been overfilled";
            throw std::exception(errorMessage.str().c_str());
        }
        this->at(currentPos++) = &Book;
    }catch(const std::exception& e){
        std::ostringstream errorMessage;
        errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3);
        throw std::exception(errorMessage.str().c_str());
    }
private:
    const char* name_;
    int currentPos;
};
class Library : private std::vector<const BookCollection*>{
public:
    void operator+=(const BookCollection& Collection){
        for(int i = 0; i < size(); ++i){
            if((*this)[i] == &Collection){
                std::ostringstream errorMessage;
                errorMessage << "The BookCollection " << Collection.name_ << " was already in the library, and therefore cannot be added";
                throw std::exception(errorMessage.str().c_str());
            }
        }
        push_back(&Collection);
    }
    void operator-=(const BookCollection& Collection){
        for(int i = 0; i < size(); ++i){
            if((*this)[i] == &Collection){
                erase(begin() + i);
                return;
            }
        }
        std::ostringstream errorMessage;
        errorMessage << "The BookCollection " << Collection.name_ << " was not found, and therefore cannot be erased";
        throw std::exception(errorMessage.str().c_str());
    }
    Book* DuplicateBook(Book* Book)const{
        return (Book->Duplicate(*Book));
    }
    void Summarize(){
        std::cout << "Library, containing " << size() << ((size() == 1) ? " book collection:" : ((size() > 0) ? " book collections:" : " book collections")) << std::endl;
        if(size() > 0){
            for(int i = 0; i < size(); ++i){
                std::cout << (*this)[i]->name_ << std::endl;
            }
        }
    }
};

Ответ 5

Вот базовый пример использования полиморфных классов

#include <iostream>

class Animal{
public:
   Animal(const char* Name) : name_(Name){/* Add any method you would like to perform here*/
    virtual void Speak(){
        std::cout << "I am an animal called " << name_ << std::endl;
    }
    const char* name_;
};

class Dog : public Animal{
public:
    Dog(const char* Name) : Animal(Name) {/*...*/}
    void Speak(){
        std::cout << "I am a dog called " << name_ << std::endl;
    }
};

int main(void){
    Animal Bob("Bob");
    Dog Steve("Steve");
    Bob.Speak();
    Steve.Speak();
    //return (0);
}

Ответ 6

Полиморфизм вообще заключается в том, что часть программы предназначена для использования в ней нескольких различных типов. Слово полиморфизм означает наличие многих форм. Полиморфизм - это механизм, который позволяет вам реализовать функцию по-разному. Полиморфизм имеет два типа: Статический полиморфизм: Статический полиморфизм - это использование полиморфизма, которое определяется при построении программы (например, в системе шаблонов на С++). После того, как программа построена, выбор сделан и известен тип. Динамический полиморфизм: Динамический полиморфизм определяется во время выполнения. (Например, указатель на базовый класс, который позволяет передавать указатели класса потомков. Базовый класс предоставляет интерфейс, а потомки реализуют этот интерфейс по-разному, которые подходят для специфики класса.) Решения принимаются во время выполнения, которые выбирают тип передачи. Важным отличием является решение при строительстве (компиляции) или времени выполнения. Как правило, хорошо продуманный статический полиморфизм работает лучше, чем динамический, поэтому предпочтительнее, когда дизайн делает это возможным. Если информация для выбора недоступна до времени выполнения, выбор является динамическим. Дополнительная информация в этой ссылке http://cplusplusinterviews.blogspot.sg/2015/04/polymorphism-intro.html

Ответ 7

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

Ответ 8

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

Ответ 9

Если кто-нибудь скажет CUT этим людям

The Surgeon
The Hair Stylist
The Actor

Что произойдет?

The Surgeon would begin to make an incision.
The Hair Stylist would begin to cut someone hair.
The Actor would abruptly stop acting out of the current scene, awaiting directorial guidance.

Итак, выше представления показывает, что в ООП есть полиморфизм (то же имя, другое поведение).

Если вы собираетесь на собеседование, а интервьюер просит вас рассказать/показать живой пример полиморфизма в той же комнате, мы сидим, скажем -

Ответ - дверь /Windows

Интересно, как?

Через дверь/окно - человек может прийти, может наступить воздух, может наступить свет, может наступить дождь и т.д.

то есть. Одно из форм различного поведения (полиморфизм).

Чтобы понять это лучше и простым способом, я использовал вышеприведенный пример. Если вам нужна ссылка для кода, следуйте приведенным выше ответам.