Dedovanje in polimorfizem sta dve najpomembnejši lastnosti objektnega programiranja. Prvo smo že spoznali, druga pa označuje princip, da lahko različni objekti razumejo isto sporočilo in se nanj odzovejo vsak na svoj način. Obe lastnosti sta osnova objektnega programiranja in omogočata hitrejši razvoj in lažje vzdrževanje programske kode.
Imejmo poleg razreda student, iz razreda oseba izpeljana še razreda profesor in asistent. Oba naj imata po en dodaten element: profesor naj ima predmet, ki ga poučuje, asistent pa predmet, pri katerem ima vaje. Vsak od njiju ima svojo inačico metode izpiši, ki izpiše podatke tega objekta.
Oglejmo si primer, ko želimo vnašati in izpisovati več objektov tipa študent, asistent ali profesor. Za shranjevanje bomo uporabili polje kazalcev. Ker ne vemo kakšne elemente bomo gradili (ta podatek bo znan šele v izvajanju), bomo shranjevali kazalce na skupni nadrazred.
#include "student.h"
#include "asistent.h"
#include "profesor.h"
#include <iostream.h>
const int max_oseb = 4;
int main()
{
oseba* ptr_polje_oseb[max_oseb]; //polje za shranjevanje oseb
oseba* ptr_začasna;
cout << "Vnesi " << max_oseb << " osebe !" << endl;
char tip_osebe[];
char ime[vel_ime];
char naslov[vel_naslov];
char smer[vel_smer];
char vaje[vel_vaje];
char predmet[vel_predmet];
for (int i = 0; i < max_oseb; i++)
{
cout << endl << endl << "Vnesi tip osebe (S, A, P): ";
cin >> tip_osebe;
cout << "Vnesi ime : ";
cin << ime;
cout << "Vnesi naslov : ";
cin >> naslov;
if (tip_osebe == 'S')
{
cout << "Smer studenta : ";
cin >> smer;
ptr_zacasna = new student (ime, naslov, smer);
}
else if (tip_osebe == 'A')
{
cout << "Predmet asistenta : ";
cin >> vaje;
ptr_zacasna = new asistent (ime, naslov, vaje);
}
else if (tip_osebe == 'P')
{
cout << "Predmet profesorja : ";
cin >> vaje;
ptr_zacasna = new profesor (ime, naslov, predmet);
}
else
{
cout << "Napacen tip osebe!" << endl;
cout << "Ustvarilo bom objekt razreda oseba!";
ptr_zacasna = new oseba (ime, naslov);
}
ptr_polje_oseb[i] = ptr_zacasna;
}
//izpis vnesenih oseb
for (i = 0; i < max_oseb; i++)
{
ptr_polje_oseb[i] ->izpisi();
cout << endl;
}
//brisanje polja kazalcev
for (i = 0; i < max_oseb; i++)
delete ptr_polje_oseb[i];
return 0;
}
Razložimo program! Najprej v zanki preberemo tip osebe ter ime in naslov osebe. Glede na tip osebe preberemo še smer, vaje ali predmet in ustvarimo objekt ustreznega razreda s pomočjo operatorja new. Ta nam vrne kazalec na objekt (student* ali asistent* oz. profesor*). Ta kazalec shranimo v spremenljivko ptr_zacasna, ki pa je tipa oseba*. Kako lahko to storimo? To je mogoče zato, ker je vsak študent (oz. asistent ali profesor) tudi oseba, zato lahko napravimo pretvorbo kazalca: npr. student* v oseba*. Pretvorba se napravi implicitno pri operatorju new. Pretvorbo bi lahko zahtevali tudi eksplicitno z naslednjim zapisom:
ptr_zacasna = (oseba*)new student(ime, naslov, smer);
Kadar kot tip osebe vnesemo nepravilno vrednost, ustvarimo objekt tipa oseba. Ko smo vnesli vse osebe, jih še izpišemo in nato zbrišemo (sproščanje dinamično dodeljenega pomnilnika!)
Prikaz delovanja programa:
Vnesi 4 osebe!
Vnesi tip osebe (S, A, P): S
Vnesi ime: Janez
Vnesi naslov: Mladinska
Smer studenta : Anglescina
Vnesi tip osebe (S, A, P): A
Vnesi ime: Metka
Vnesi naslov: Slovenska
Predmet asistenta: Nemscina
Vnesi tip osebe (S, A, P): P
Vnesi ime: France
Vnesi naslov: Ljubljanska
Predmet profesorja: Računalnistvo
Vnesi tip osebe (S, A, P): k
Vnesi ime: Marija
Vnesi naslov: Betnavska
Napacen tip osebe!
Ustvaril bom objekt razreda oseba!
Ime osebe: Janez
Naslov: Mladinska
Ime osebe: Metka
Naslov: Slovenska
Ime osebe: France
Naslov: Ljubljanska
Ime osebe: Marija
Naslov: Betnavska
Če si ogledamo izpis programa opazimo, da so se za vsak vnešeni objekt izpisali samo podatki, ki so zapisani v razredu oseba in ne vsi podatki študenta, asistenta in profesorja. Zakaj?
Ko smo objekte shranjevali, smo kazalec pretvorili v tip oseba*. Ko kličemo metodo izpisi, zato kličemo metodo oseba::izpisi. Vidimo, da navadna redefinicija metode v tem primeru ne zadošča. Čeprav imamo samo kazalce na razred oseba, želimo, da vsak objekt ohrani sebi lastno obnašanje ob klicu metode izpisi. Uporabiti moramo polimorfno redefinicijo.
Polimorfno redefinicijo omogočimo z definicijo virtualne metode. Metodo izpisi moramo v razredu oseba definirati kot virtualno, kar storimo na naslednji način:
virtual void izpisi() const;
Ta definicija pove, da naj izpeljani objekt kliče svojo funkcijo izpisi, čeprav bi bil klic izvršen preko kazalca na nadrazred (oseba*). Ob takšni definiciji metode izpisi v razredu oseba, bo prejšnji primer izpisal naslednje:
Vnesi 4 osebe!
Vnesi tip osebe (S, A, P): S
Vnesi ime: Janez
Vnesi naslov: Mladinska
Smer studenta: Anglescina
Vnesi tip osebe (S, A, P): A
Vnesi ime: Metka
Vnesi naslov: Slovenska
Predmet asistenta: Nemscina
Vnesi tip osebe (S, A, P): P
Vnesi ime: France
Vnesi naslov: Ljubljanska
Predmet profesorja: Računalnistvo
Vnesi tip osebe (S, A, P): k
Vnesi ime: Marija
Vnesi naslov: Betnavska
Napacen tip osebe!
Ustvaril bom objekt razreda oseba!
Ime osebe: Janez
Naslov: Mladinska
Smer: Angleščina
Ime osebe: Metka
Naslov: Slovenska
Vaje pri: Nemščina
Ime osebe: France
Naslov: Ljubljanska
Predmet: Računalništvo
Ime osebe: Marija
Naslov: Betnavska
Problem se pojavi tudi pri brisanju dinamično alociranih objektov. Pri klicu operatorja delete se bo klical destruktor za razred oseba namesto destruktor ustreznega objekta. Zato moramo tudi destruktor definirati kot virtualno metodo:
virtual ~oseba();