Polimorfizm jest ogromną zaletą programowania obiektowego, daje on programiście możliwość elastycznego zarządzania obiektami w trakcie pisania programu. Polimorfizm jest powiązany z metodami wirtualnymi. Złe zrozumienie i co za tym idzie niewłaściwe operowanie mechanizmem polimorfizmu może skutkować nietypowymi błędami, trudnymi do diagnostyki problemami, a w najlepszym przypadku być przyczyną znacznego spowolnienia działania programu.

Przykładem polimorfizmu jest poniższy kod:

#include <iostream>
#include <cstdlib>
#include <windows.h>

using namespace std;
class Pojazd {
public:
    virtual void zatrzymaj() {
        cout << "zatrzymuję pojazd..?\n";
    }
};

class Samochod :public Pojazd {
public:
    void zatrzymaj() {
        cout << "Zatrzymuję samochód.\n";
    }
};

class Rower :public Pojazd {
public:
    void zatrzymaj() {
        cout << "Zatrzymuję rower.\n";
    }
};

class Statek :public Pojazd {
public:
    void zatrzymaj() {
        cout << "zatrzymuję statek\n\n";
    }
};

int main()
{
    SetConsoleCP( 852 );
    setlocale ( LC_ALL, "" );

    Pojazd **tablica = new Pojazd*[3];

    tablica[0] = new Samochod();
    tablica[1] = new Rower();
    tablica[2] = new Statek();

    for (int i = 0; i<3; i++) {
        tablica[i]->zatrzymaj();
    }

    return 0;
}

Kod powyższy wprowadza nowe zagadnienia takie jak metody i klasy wirtualne (poprzedzone selektorem virtual). Metody wirtualne nie muszą być definiowane w ramach klasy – ale to jednocześnie implikuje fakt, że nie da się powołać instancji takiej klasy do życia. Można za to odziedziczyć z takiej klasy i wtedy uzupełnić definicję takiej metody. Można też i to jest ta wielka zaleta powołać do życia tablicę wskaźników na obiekty będące potomnymi do klasy bazowej – i to tutaj zostało uczynione. Stworzona tablica wskaźników klasy Pojazd nie jest błędna, bowiem nie stworzyliśmy obiektów klasy Pojazd (bo się nie da skoro są wirtualne), ale wskazaliśmy rodzinę obiektów należącą do klasy Pojazd, czyli wszystkie te, które z niej dziedziczyły. Taki zabieg pozwala na wprowadzenie do tablicy wskaźników na obiekty potomne, a następnie użycie ich pomimo, że są diametralnie różne.

W powyższym przykładzie mamy cztery klasy gdzie klasa Pojazd jest wirtualna, a następne trzy po niej dziedziczą. W każdej MUSI być zdefiniowana metoda zatrzymaj() bowiem oznaczyliśmy ją jako wirtualną w obiekcie bazowym. Dlatego też można ją uruchomić za pomocą pętli w każdym obiekcie w jednym akcie programistycznym. Gdyby nie było polimorfizmu nasz kod mógłby wyglądać następująco:

#include <iostream>
#include <cstdlib>
#include <windows.h>

using namespace std;

class Samochod  {
public:
    void zatrzymaj() {
        cout << "Zatrzymuję samochód.\n";
    }
};

class Rower  {
public:
    void zatrzymaj() {
        cout << "Zatrzymuję rower.\n";
    }
};

class Statek {
public:
    void zatrzymaj() {
        cout << "zatrzymuję statek\n\n";
    }
};

int main()
{
    SetConsoleCP( 852 );
    setlocale ( LC_ALL, "" );


    Samochód poj1;
    Rower poj2;
    Statek poj3;

    poj1.zatrzymaj();
    poj2.zatrzymaj();
    poj3.zatrzymaj();

    return 0;
}

I jak, czy działanie programu zmieniło się w efekcie na inne?? Nie, wygląda identycznie! Prawda?!

No OK, więc czym się to różni, że polimorfizm jest tak uznawany za rewolucyjny mechanizm?!

W pierwszym przypadku powołaliśmy do życia tablicę wskaźników (w sumie nie ma znaczenia, czy to będzie tablica statyczna, dynamiczna, czy też jakakolwiek inna lista) do której wpisaliśmy 3 wskaźniki na nowo stworzone obiekty i uruchomiliśmy pętlę, która “omiotła” całą tablicę uruchamiając metody zatrzymaj() w każdym przeglądanym elemencie tablicy; w drugim przypadku… stworzyliśmy 3 nowe obiekty i wywołaliśmy ich metody zatrzymaj(). Osiągnęliśmy tyle samo… ale teraz wyobraźmy sobie, że mamy flotę naszych pojazdów liczoną w tysiącach pojazdów – i jak teraz wygląda nasz kod? W drugim przypadku nasz kod zwiększa się o… tysiące linii deklaracji nowych obiektów i tysiące linii uruchomienia metody zatrzymaj(), jeśli jakiś obiekt się zmieni… musimy fizycznie zmienić kod używając innej klasy. A w pierwszym przypadku?! Nie zmieni się nic… po prostu do tablicy trzeba będzie wpisać tylko raz… kolejny wskaźnik na nowo dopisywany do bazy obiekt. Reszta bez zmian… Tutaj widać potęgę polimorfizmu. Prawda, że odkrywcze?!