Podstawy
programowania
obiektowego
Ćwiczenia laboratoryjne nr 4
Temat: Dziedziczenie, metody wirtualne
Prowadzący:
mgr inż. Dariusz Rataj
Koszalin 2001
Podstawy programowania obiektowego ćw nr 2
Strona 2
Spis treści:
1. Dziedziczenie, klasy bazowe i pochodne
2. Metody wirtualne
1. Dziedziczenie, klasy bazowe i pochodne
Dziedziczenie oznacza możliwość tworzenia nowych klas na podstawie klas już istniejących.
Inaczej mówiąc, dziedziczenie polega na przejmowaniu właściwości jednej klasy (klasy
bazowej) przez inna klasę (klasę pochodną), np. możemy utworzyć klasę bazową
samochod i klasy pochodne ciezarowka i osobowy. Klasa samochod powinna posiadać
atrybuty (pola i metody) wspólne dla wszystkich samochodów a klasy ciezarowka i osobowy
atrybuty specyficzne dla konkretnego rodzaju samochodu. Klasy pochodne rozszerzają
możliwości klasy bazowej. Rozwijając przykład można by utworzyć klasy pochodne klas
ciezarowka i osobowy i utworzyć bardziej szczegółowy podział np. klasy klasa_S,
klasa_rodzinny, klasa_Maluch, TIR, dzwig itd.
Rys.1. Hierarchia dziedziczenia - drzewo klas samochodów
W sytuacji gdy klasa pochodna jest klasą bazową dla innych klas mówimy o dziedziczeniu
wielokrotnym. W języku C++ dziedziczenie możemy zrealizować umieszczając w nagłówku
klasy, po dwukropku, listy nazw klas bazowych oddzielonych przecinkami, np.:
class A {...};
class B: A {...}; // klasa B dziedziczy po klasie A
class C: B {...}; // klasa C dziedziczy po klasie B
Umieszczając po dwukropku więcej niż jedną klasę:
class A {...};
class B {...};
class C: A, B {...}; // klasa C dziedziczy po klasie A i klasie B
klasa pochodna dziedziczy komponenty po dwóch (lub więcej) klasach.
Przed nazwą klasy bazowej możemy umieścić modyfikator prawa dostępu:
public
i
private
. Modyfikator public uczyni z publicznych i chronionych komponentów klasy
bazowej publiczne i chronione komponenty klasy pochodnej. Modyfikator private uczyni
z publicznych i chronionych komponentów klasy bazowej prywatne komponenty klasy
pochodnej. Komponenty prywatne klasy bazowej nie będą dostępne dla klasy pochodnej,
np.:
class A {...};
class B {...};
class C: public A, private B {...}; // C dziedziczy po klasie A i klasie B
samochod
osobowy
ciezarowka
klasa_S
klasa_rodzinny
TIR
dzwig
klasa_Maluch
Podstawy programowania obiektowego
Strona 3
Komponenty publiczne i chronione klasy A będą publicznymi i chronionymi komponentami
klasy C. Komponenty publiczne i chronione klasy B będą komponentami prywatnymi klasy C
(nie będą dostępne dla klas pochodnych klasy C). Dziedziczenie po kilku klasach nazywamy
dziedziczeniem wielobazowym.
2. Metody wirtualne
Metody wirtualne pozwalają na odwoływanie się do nieistniejących jeszcze metod klas
pochodnych, które zamierzamy utworzyć w przyszłości. W przykładzie poniżej (patrz:
Przykład 1) klasa bazowa osoba posiada metodę czysto wirtualną (bez definicji działania
metody) drukuj. Jest to metoda która istnieje tylko w celu nadpisania w klasach
pochodnych. Klasy pochodne pracownik i student nadpisują metodę wirtualną drukuj
definiując jej działanie odpowiednio dla każdej klasy. W programie głównym deklarowany
wskaźnik ktos na zmienną obiektową typu osoba jest inicjowany dynamicznie przez
wywołanie konstruktora klasy pracownik a potem student.
osoba *ktos = new pracownik(
"Ewa Grabowska"
,
30
,
10
);
ktos->drukuj();
delete ktos;
ktos = new student(
"Marek Jankowski"
,
20
,
2
);
ktos->drukuj();
delete ktos;
Po każdym zainicjowaniu obiektu wywoływana jest metoda drukuj. Metoda jest wywoływana
na rzecz klasy osoba - pozornie!!! - w rzeczywistości wywoływane są odpowiednie metody
wirtualne drukuj klas pracownik i student. Dzieje się tak dzięki zadeklarowaniu metody
drukuj jako metody wirtualnej - w przeciwnym przypadku wywołanie drukuj powodowałoby
próbę wywołania metody składowej klasy osoba (która nie ma definicji tylko deklarację).
Metody wirtualne stosowane są przede wszystkim w celu rozszerzania możliwości
klas w przyszłości. W strukturach gdzie klasa bazowa posiada wiele klas pochodnych jest to
często jedyna metoda na uporządkowanie pracy ze złożonym systemem obiektów.
Przykładowo, znane wszystkim obiekty kontrolne systemu Windows (TButton, TLabel,
TMemo, TList itd.) dziedziczą po klasie TWinControl (dziedziczenie wielokrotne). Wszystkie
te obiekty posiadają inną reprezentacje graficzną. Przy każdym rysowaniu okna aplikacji
Windows wywoływana jest metoda rysująca dla wszystkich obiektów kontrolnych - metoda
Repaint klasy TWinControl. Ponieważ jest to metoda wirtualna i każda klasa pochodna
TWinControl ją nadpisuje, każdy z obiektów jest rysowany inaczej.
Metody wirtualne są praktyczną realizacją polimorfizmu w programowaniu
obiektowym (polimorfizm - wielopostaciowość). O metodach wirtualnych można powiedzieć
że są to metody, które mają wiele postaci (definicji).
Podstawy programowania obiektowego ćw nr 2
Strona 4
Przykład 1. Definicja klasy bazowej osoba. Klasa zawiera pola chronione: nazwisko, wiek; metodę
czysto wirtualną drukuj. Klasy pochodne pracownik i student posiadają pola specyficzne odpowiednio
dla pracownika i studenta: stazpracy i rokstudiow. Jednocześnie metoda wirtualna drukuj została
nadpisana w obydwu klasach pochodnych.
#include <conio.h>
// clrscr, getch
#include <iostream.h>
// cout
#include <string.h>
// strcpy
// klasa bazowa abstrakcyjna
class
osoba
{
public
:
osoba(
char
*nazw,
int
w);
virtual
void
drukuj() =
0
;
// funkcja czysto wirtualna
protected
:
char
nazwisko[
20
];
int
wiek;
};
// klasa pochodna - pracownik
class
pracownik :
public
osoba
{
public
:
pracownik(
char
*nazw,
int
w,
int
staz);
virtual
void
drukuj();
private
:
int
stazpracy;
};
// klasa pochodna - student
class
student :
public
osoba
{
public
:
student(
char
*nazw,
int
w,
int
rok);
virtual
void
drukuj();
private
:
int
rokstudiow;
};
// definicje metod - klasa osoba
osoba::osoba(
char
*nazw,
int
w)
{
strcpy(nazwisko, nazw);
wiek = w;
}
// definicje metod - klasa pracownik
pracownik::pracownik(
char
*nazw,
int
w,
int
staz) : osoba(nazw, w)
{
stazpracy = staz;
}
void
pracownik::drukuj()
{
cout <<
"Nazwisko: "
<< nazwisko <<
", "
;
cout <<
"wiek: "
<< wiek <<
", "
;
cout <<
"staz pracy: "
<< stazpracy << endl;
}
// definicje metod - klasa student
student::student(
char
*nazw,
int
w,
int
rok) : osoba(nazw, w)
{
rokstudiow = rok;
}
void
student::drukuj()
{
cout <<
"Nazwisko: "
<< nazwisko <<
", "
;
cout <<
"wiek: "
<< wiek <<
", "
;
cout <<
"rok studiow: "
<< rokstudiow << endl;
}
// program glowny
void
main()
Podstawy programowania obiektowego
Strona 5
{
clrscr();
// wskaznik na obiekt klasy bazowej abstrakcyjnej
osoba *ktos = new pracownik(
"Ewa Grabowska"
,
30
,
10
);
// dynamiczne wywolywanie funkcji wirtualnej drukuj
ktos->drukuj();
delete ktos;
ktos = new student(
"Marek Jankowski"
,
20
,
2
);
ktos->drukuj();
delete ktos;
cout << endl <<
"Nacisnij dowolny klawisz..."
;
getch();
}
Zadania do wykonania na zajęciach i w domu:
1. Utworzyć prosty system grafiki wektorowej rysujący różnego rodzaju obiekty graficzne. Większość
obiektów graficznych możemy wpisać w prostokąt (okrąg, elipsa, linia, prostokąt itd.). System
powinien definiować:
•
klasę bazową dla obiektów graficznych - graphObject, posiadającą pola chronione x, y -
współrzędne lewego rogu prostokąta; pola chronione height i width (wysokość i szerokość
prostokąta); metodę czysto wirtualną rysuj(); pola definiujące kolor itp.;
•
klasy pochodne klasy graphObject: linia, elipsa, okrag, prostokat. Klasy powinny nadpisywać
metodę rysuj() klasy bazowej i odpowiednio rysować obiekt wpisany w prostokąt.
Program testujący zadeklaruje tablicę wskaźników na zmienne typu graphObject:
graphObject* obiekty[20];
a następnie utworzy obiekty graficzne i narysuje, np. tak:
for (int i = 0; i<20; i+=4)
{
obiekty[i] =
new linia(...); // parametry zależne od konstruktora
obiekty[i+1] = new elipsa(...);
obiekty[i+2] = new okrag(...);
obiekty[i+3] = new prostokat(...);
}
for (int i = 0; i<20; i++) obiekty[i]->rysuj();
....
for (int i = 0; i<20; i++) delete obiekty[i];
Obiekty graficzne można zainicjować inaczej wg uznania. Gdy rozszerzymy możliwości klas o ruch
po ekranie, możliwośc skalowania obiektów to BĘDZIE TO!!!
2. Zrealizować strukturę klas jak w punkcie 1 instrukcji (klasa bazowa samochod, pochodne ...).
3. Utworzyć klasę bazową Button (klawisz, przycisk) i klasy pochodne graphButton i textButton. W
zależności od trybu pracy programu (graficzny, tekstowy) przycisk Button będzie miała inną
reprezentację (graficzną lub tekstową).