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


AFAIK:

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

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

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

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

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

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

  • полиморфизмом
  • параметрический полиморфизм элемент сайт говорит,

полиморфизмом:

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

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

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

Я их с трудом понимаю: (

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

7 112

7 ответов:

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

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

    Type1 x;
    Type2 y;

    f(x);
    f(y);

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

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


C++ механизмы полиморфизма

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

можно писать 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
    

другие сопутствующие механизмы

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

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

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

дальнейшая категоризация

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

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

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

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

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

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

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

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

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

Альф Штайнбах комментирует, что в стандарте C++полиморфных относится только к полиморфизму времени выполнения с использованием виртуальной отправки. Генерал Комп. Научный. значение является более инклюзивным, согласно глоссарию создателя C++ Бьярне Страуструпа (http://www.stroustrup.com/glossary.html):

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

этот ответ-как и вопрос-относится к функциям C++ для Comp. Научный. терминология.

Обсуждение

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

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

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

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

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

костюмы полиморфизма времени выполнения:

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

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

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

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

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

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

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

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

> неявные компиляторные перегрузки

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

  • тип-конкретные инструкции ЦП
  • результат того же типа.

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

std::string x;
int y = 0;

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

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

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

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

первый пункт обобщает красиво (из старого проекта - надеюсь, все еще существенно правильно):

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

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

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

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

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

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

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

a(3.14);
a(42);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

возвращаясь к параметрическому и ad-hoc полиморфизму, эти термины больше популярны в функциональном программировании, и они не совсем работают в C++. Несмотря на это...

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

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

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

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

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

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

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

в например, Хаскелл, ты можешь взять...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

главная.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

главная.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_ << "'s 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;
            }
        }
    }
};

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

#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);
}

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

Если кто-нибудь говорит вырезать этим людям

The Surgeon
The Hair Stylist
The Actor

что будет?

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

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

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

Ответ - Двери / Окна

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

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

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

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