programowania
obiektowego
Ćwiczenia laboratoryjne nr 4
Temat: Dziedziczenie, metody wirtualne
Prowadzący:
mgr inż. Dariusz Rataj
Koszalin 2001
Podstawy programowania obiektowego ćw nr 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.
samochod
osobowy
ciezarowka
klasa_S
klasa_rodzinny
klasa_Maluch
dzwig
TIR
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
Strona 2
Podstawy programowania obiektowego
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).
Strona 3
Podstawy programowania obiektowego ćw nr 2
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()
Strona 4
Podstawy programowania obiektowego
{
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ą).
Strona 5