Podstawy
programowania
obiektowego
Ćwiczenia laboratoryjne nr 1
Temat: Wstęp do programowania obiektowego
Prowadzący:
mgr inż. Dariusz Rataj
Koszalin 2001
Podstawy programowania obiektowego
Strona 2
Skrócony spis treści:
Część 1 - Programowanie obiektowe w języku C++
Część 2 - Programowanie obiektowe w środowisku Borland C++Builder
1. Programowanie obiektowe w języku C++
1.1. Cel programowania obiektowego
Programowanie obiektowe jest próbą "przeniesienia" rzeczywistości na platformę maszyn liczących zwanych
komputerami. Człowiek w życiu codziennym postrzega przedmioty (obiekty) rzeczywiste w sposób intuicyjny nie
zwracając uwagi na szczegóły zachowania czy stanu przedmiotu. W programowaniu strukturalnym programista jest
zmuszony do znajomości wielu parametrów symulowanego obiektu. Celem programowania obiektowego jest
uzyskanie możliwości wydawania poleceń dla symulowanego obiektu rzeczywistego bez konieczności zagłębiania się
w szczegóły jego konstrukcji czy stanu.
1.2. Przykład symulacji obiektu rzeczywistego
W tej części przedstawiony zostanie prosty przykład opisu obiektu rzeczywistego - samochodu. Widząc samochód
jadący ulicą przeciętny człowiek zwróci uwagę jedynie na ogólne cechy pojazdu, np.: wielkość (duży, mały), kolor
itd. Jadąc samochodem "wydajemy polecenia": przyspiesz, zwolnij, zatrzymaj itd. Opis obiektu rzeczywistego na
poziomie języka programowania powinien umożliwić wydawanie tego typu poleceń. Każdy obiekt rzeczywisty
posiada trzy elementy: tożsamość (nazwa), stan lub cechy obiektu, zachowanie (działania na obiekcie).
Przykładowy samochód jest oczywiście obiektem rzeczywistym złożonym z wielu podzespołów, części i możemy
opisać go bardzo szczegółowo. W wielu zastosowaniach wystarczy nam jednak opis tylko niektórych cech obiektu.
Takie podejście do opisu (pewne cechy są istotne inne są bez znaczenia) określane są w teorii obiektowej mianem
abstrakcji. Programista operuje jedynie na pewnym poziomie abstrakcji niezbędnym do zrealizowania celu
oprogramowania, np. inaczej będzie wyglądał opis obiektu programisty piszącego grę "wyścigi samochodowe", a
inaczej opis do celów symulacji wypadków wykonany dla firmy produkującej samochody. Poniżej przedstawiony
został prosty przykład opisu obiektowego (obiektu - samochodu) demonstrujący zasady tworzenia symulacji
obiektu rzeczywistego.
Prezentowany przykład ogranicza się tylko do elementarnych cech samochodu: marka, model, kolor, pozX (pozycja
X), pozY (pozycja Y) i czterech czynności: ustawMarke, ustawModel, ustawKolor, przesun operujących na cechach.
W terminologii programowania obiektowego cechy opisujące stan obiektu będziemy nazywać polami obiektu (lub
zmiennymi składowymi obiektu) natomiast czynności nazwiemy metodami (lub procedurami i funkcjami
składowymi obiektu).
1. Tożsamość (nazwa) - samochód
2. Stan obiektu (cechy opisujące obiekt):
- Marka
- Model
- Kolor
- Pozycja (współrzędne x i y)
3. Zachowanie (działania na obiekcie):
- Nazwanie marki
- Nazwanie modelu
- Zmiana koloru
- Przesunięcie samochodu - zmiana
położenia (pozycji)
Obiekt rzeczywisty
Opis obiektu
rzeczywistego
class
samochod {
public
:
char
marka[
20
];
char
model[
20
];
int
kolor;
int
pozX, pozY;
samochod();
void
ustawMarke(
char
*s);
void
ustawModel(
char
*s);
void
ustawKolor(
int
k);
void
przesun(
int
x,
int
y);
};
Definicja typu
obiektowego
(Zgodnie ze składnią języka
programowania C++)
Podstawy programowania obiektowego
Strona 3
Po zdefiniowaniu obiektu możemy wykorzystać obiekt w programie głównym. Na początek należy zdefiniować
zmienną obiektową klasy samochod:
samochod car1;
// deklaracja obiektu (utworzenie instancji obiektu)
Następnie w ciele programu głównego możemy wywołać wcześniej zdefiniowane funkcje i procedury obiektu.
Przykładowy program główny symuluje jazdę samochodu po ekranie monitora (kursory: góra, dół, prawo, lewo).
Pełny kod programu definiującego i wykorzystującego obiekt
samochod:
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
#include <string.h>
class
samochod {
public
:
// pola (zmienne)
char
marka[
20
];
char
model[
20
];
int
kolor;
int
pozX, pozY;
// metody (funkcje)
samochod();
// konstruktor
void
ustawMarke(
char
*s);
// ustawienie marki samochodu
void
ustawModel(
char
*s);
// ustawienie marki samochodu
void
ustawKolor(
int
k);
// ustawienie koloru samochodu
void
przesun(
int
x,
int
y);
// przesuniecie po ekranie
};
samochod::samochod()
{
pozX =
0
; pozY =
0
;
}
void
samochod::ustawMarke(
char
*s)
{
strcpy(marka, s);
// kopiowanie parametru do zmiennej marka
cout <<
"Marka:"
<< marka << endl;
}
void
samochod::ustawModel(
char
*s)
{
strcpy(model, s);
// kopiowanie parametru do zmiennej model
cout <<
"Model:"
<< model << endl;
}
void
samochod::ustawKolor(
int
k)
{
kolor = k;
// zmiana zmiennej kolor
textcolor(k);
// zmiana koloru znaku na ekranie
cout <<
"Kolor(nr):"
<< kolor << endl;
}
void
samochod::przesun(
int
x,
int
y)
{
pozX += x; pozY += y;
// zmiana zmiennych pozX i pozY
if
(pozX >
80
) pozX=
1
;
if
(pozY >
25
) pozY=
1
;
Podstawy programowania obiektowego
Strona 4
if
(pozX <
1
) pozX=
80
;
if
(pozY <
1
) pozY=
25
;
gotoxy(pozX, pozY);
// przesuniecie kursora
cprintf(
"%c"
,
'X'
);
}
void
main()
{
int
key;
clrscr();
samochod car1;
// utworzenie obiektu typu samochod
car1.ustawMarke(
"Audi"
);
car1.ustawModel(
"A4"
);
car1.ustawKolor(RED);
car1.przesun(
40
,
12
);
do
{
key = getch();
// pobranie kodu klawisza
switch
(key) {
case
72
: car1.przesun(
0
,-
1
);
break
;
// kody kursorow: gora
case
80
: car1.przesun(
0
,
1
);
break
;
// dol
case
77
: car1.przesun(
1
,
0
);
break
;
// prawo
case
75
: car1.przesun(-
1
,
0
);
break
;
// lewo
}
}
while
(key !=
27
);
// rozne od Esc
textcolor(WHITE);
clrscr();
}
2. Programowanie obiektowe w C++ Builder
2.1. Środowisko programowania pod Windows - C++ Builder
Nowoczesne środowiska programowania pod system Windows takie jak Borland C++ Builder
umożliwiają stworzenie programu okienkowego wykorzystującego gotowe obiekty zdefiniowane
w środowisku graficznym systemu operacyjnego lub w samym środowisku C++Builder. W większości
prostych zastosowań nie będzie konieczne "ręczne" definiowanie własnych obiektów tak jak ma to
miejsce w programowaniu strukturalnym. Większość mozolnej pracy (tworzenie plików, okien,
obiektów kontrolnych itp.) wykonuje za nas środowisko programowania. Proces tworzenia aplikacji
Windows w tego typu środowiskach można ująć w kilku podstawowych etapach:
a) Utworzenie projektu (nazwanie projektu, wskazanie katalogu w którym przechowywane będą
wszystkie pliki składające się na projekt). Na tym etapie tworzone jest (automatycznie) główne
okno aplikacji i główne pliki źródłowe aplikacji.
b) Dodanie obiektów kontrolnych do głównego okna aplikacji (menu, napisy, pola tekstowe,
przyciski itp.).
c) Utworzenie okien dodatkowych posiadających również obiekty kontrolne, które należy
zdefiniować (podobnie jak w głównym oknie aplikacji: patrz pkt. 2 i 3). Okna dodatkowe
najczęściej są aktywowane przez okno główne aplikacji (są aktywowane w metodach obsługi
zdarzeń obiektów kontrolnych), np.: zdarzenie "kliknięcie na przycisku" w głównym oknie aplikacji
spowoduje uruchomienie (aktywowanie) okna dodatkowego.
d) Zdefiniowanie odpowiednich "zdarzeń" i przypisanie ich do obiektów kontrolnych, np.
zdefiniowanie metody (funkcji, procedury), która zostanie wykonana po kliknięciu na przycisku.
e) Skompilowanie i uruchomienie (jeżeli nie ma błędów) oraz przetestowanie działania aplikacji.
Podstawy programowania obiektowego
Strona 5
2.2. Przykład prostej aplikacji w CBuilder - "Ankieta"
Program "Ankieta" będzie umożliwiał przeprowadzenie prostej ankiety osobowej (np. ucznia, studenta)
zawierającej pytania o imię, nazwisko, wiek i klasa. Po wypełnieniu ankiety użytkownik może
wygenerować podsumowanie wykorzystujące dane z ankiety. Podsumowanie zostanie umieszczone w
dodatkowym oknie aplikacji a następnie zapisane do pliku o nazwie wybranej przez użytkownika. Do
zapisania pliku o wybranej nazwie i w wybranym katalogu na dysku można użyć standardowego
obiektu SaveDialog z grupy obiektów Dialogs.
a) Utworzenie projektu
Z menu górnego wybieramy polecenie File - New Application. Następnie polecenie File - Save
Project as.... Otworzy się okno dialogowe w którym wybieramy katalog naszego projektu i nazwę
plików (pliku źródłowego Unit1.cpp - plik okna głównego aplikacji; pliku projektu Project1.bpr). Po
wykonaniu tych czynności utworzy się puste okno główne aplikacji gotowe do edycji o nazwie Form1.
Okno główne aplikacji
Okno zapisu do pliku (okno
standardowe z grupy Dialogs)
Okno dodatkowe aplikacji
Podstawy programowania obiektowego
Strona 6
b) Dodanie obiektów kontrolnych do głównego okna aplikacji
Dodawane obiekty kontrolne możemy wybrać z podręcznego menu obiektów. W głównym oknie
aplikacji "Ankieta" użyjemy obiektów kontrolnych z grupy Standard:
-
Label (tekst statyczny).
-
Edit (pole tekstowe służące do wprowadzania krótkich tekstów).
-
Button (graficzny przycisk)
c) Utworzenie okien dodatkowych - okno "Wynik ankiety"
Aby dodać okno do aplikacji wybieramy polecenie File - New Form. Utworzy się nowy obiekt okna o
nazwie Form2 (plik źródłowy Unit2.cpp). Będzie to drugie okno aplikacji - "Wynik ankiety". W drugim
oknie ("Wynik ankiety") aplikacji użyjemy obiektów kontrolnych z grupy Standard:
-
GroupBox (ramka grupująca obiekty).
-
Memo (pole tekstowe służące do wprowadzania dłuższych tekstów złożonych z kilku linii tekstu).
-
Button (graficzny przycisk) x 2.
Dodatkowo w tym oknie dodamy standardowe okno SaveDialog z grupy Dialogs (poszukać tego
obiektu). Obiekt ten (okno dialogowe zapisu do pliku) będzie nazywał się SaveDialog1.
Dodajemy także przycisk (Button) "Zamknij".
d) Zdefiniowanie zdarzeń i przypisanie ich do obiektów kontrolnych
- zdarzenia w głównym oknie aplikacji
W oknie głównym aplikacji (obiekt Form1 - "Ankieta") zdefiniujemy jedno zdarzenie - kliknięcie na
przycisku "Zapisz do pliku". W tym celu klikamy na przycisku "Zapisz do pliku" - w oknie Object
Inspector pojawią się właściwości obiektu Button. Przechodzimy do zakładki Events (zdarzenia) i
klikamy dwa razy na zdarzeniu OnClick (kliknięcie na obiekcie). Środowisko programistyczne powinno
utworzyć automatycznie metodę obsługi zdarzenia OnClick o nazwie Button1Click. Teraz definiujemy
tą metodę wpisując wewnątrz fragment kodu w języku C++:
Form1->Memo1->Text = Edit1->Text +" nazywa się "+ Edit2->Text +", ma "+
Edit3->Text +" lat i chodzi do klasy " + Edit4->Text;
Form2->ShowModal();
-
Pierwsze polecenie zmienia pole Text obiektu Memo1 okna Form2. W tym poleceniu składamy
tekst obiektu Memo1 odczytując dane (tekst) z pól Edit okna głównego. Każdy obiekt typu Edit
posiada pole Text przechowujące dane wpisane do obiektu, np.: Edit1->Text przechowuje tekst
wpisany w obiekcie Edit1 (imię ankietowanego).
-
Drugie polecenie uaktywnia drugie okno w trybie modalnym.
Wszystkie okna systemu Windows mogą pracować w dwóch podstawowych trybach:
-
Modalnym (aktywacja procedurą ShowModal),
-
Standardowym lub niemodalnym (aktywacja procedurą Show),
Label
Edit
Button
Button
Memo
GroupBox
Podstawy programowania obiektowego
Strona 7
- zdarzenia w oknie "Wynik ankiety"
W oknie dodatkowym aplikacji (obiekt Form2 - "Wynik ankiety") zdefiniujemy jedno zdarzenie -
kliknięcie na przycisku "Podsumowanie". W tym celu klikamy na przycisku "Podsumowanie" - w oknie
Object Inspector pojawią się właściwości obiektu Button. Przechodzimy do zakładki Events
(zdarzenia) i klikamy dwa razy na zdarzeniu OnClick (kliknięcie na obiekcie). Środowisko
programistyczne powinno utworzyć automatycznie metodę obsługi zdarzenia OnClick o nazwie
Button1Click. Teraz definiujemy tą metodę wpisując wewnątrz fragment kodu w języku Delphi:
if (SaveDialog1->Execute()) Memo1->Lines->SaveToFile(SaveDialog1->FileName);
Obiekt SaveDialog posiada metodę Execute, która uruchamia okno dialogowe wyboru pliku na
dysku. Po zakończeniu działania okna w polu (zmiennej) FileName przechowywana jest pełna
(z pełną ścieżką dostępu) nazwa wybranego pliku. Metoda SaveToFile zapisuje tekst obiektu Memo
do wybranego pliku.
W oknie "Wynik ankiety" znajduje się (patrz: punkt c) przycisk "Zamknij". Dla tego przycisku
definiujemy pole (właściwość) ModalResult równe mrOk. MrOk jest jednym z kilku poleceń okna
otwartego w trybie modalnym i powoduje "potwierdzenie" działań wykonanych w oknie. W naszym
przykładzie spowoduje zamknięcie okna.
e) Skompilowanie, uruchomienie i przetestowanie aplikacji
Po wykonaniu wszystkich wyżej opisanych działań możemy skompilować i uruchomić aplikację. W tym
celu wybieramy polecenie z menu górnego Run - Run. Następnie należy sprawdzić czy wszystkie
zaprogramowane działania wykonują się poprawnie:
-
wypełniamy ankietę,
-
klikamy przycisk "Podsumowanie",
-
klikamy przycisk "Zapisz do pliku" okna "Wynik ankiety",
-
wybieramy katalog i podajemy nazwę pliku do którego chcemy zapisać dane - polecenie "Zapisz"
okna SaveDialog,
-
teraz można pozamykać okna i zakończyć działanie aplikacji,
-
korzystając z dowolnego edytora tekstowego systemu Windows (np. Notatnik) sprawdzamy: czy
został poprawnie utworzony plik tekstowy i czy zawiera poprawny tekst wynikowy ankiety.
2.3. Zadanie do samodzielnego wykonania - "Prosta przeglądarka
graficzna"
Utworzyć program (aplikację) Windows:
Obiekt
Image
Menu górne
obiekt typu
Menu
Grafika
umieszczona
w obiekcie
Image
Podstawy
programowania
obiektowego
Ćwiczenia laboratoryjne nr 2
Temat: Konstruktory, destruktory, funkcje zaprzyjaźnione
Prowadzący:
mgr inż. Dariusz Rataj
Koszalin 2001
Podstawy programowania obiektowego ćw nr 2
Strona 2
Spis treści:
1. Konstruktor przeciążony, destruktor obiektu
2. Hermetyzacja - ukrywanie informacji
1. Konstruktor przeciążony, destruktor obiektu
Dla każdej klasy mamy możliwość utworzenia kilku konstruktorów. Może to być konstruktor
bezparametrowy i konstruktor z parametrami. Jeżeli klasa nie posiada zdefiniowanego
konstruktora to kompilator automatycznie doda konstruktor bezparametrowy. Możemy
także samodzielnie zdefiniować działanie konstruktora bezparametrowego. Przykład 1
definiuje klasę Plik otwierającą plik dyskowy do zapisu. Zdefiniowany został konstruktor
bezparametrowy - tworzący plik o nazwie wbudowanej (default.txt) oraz konstruktor
z parametrem - nazwą pliku dyskowego.
Deklaracja zmiennej obiektowej
Plik plik1(
"proba.txt"
);
// konstruktor z parametrem - nazwa pliku
wywoła konstruktor z parametrem i utworzy plik dyskowy o nazwie proba.txt.
Deklaracja zmiennej obiektowej
Plik plik2;
// konstruktor bezparametrowy - plik default.txt
wywoła konstruktor bezparametrowy i utworzy plik dyskowy o nazwie wbudowanej
default.txt.
Kompilator automatycznie decyduje jaki konstruktor ma być wywołany w zależności od typu
parametrów w deklaracji obiektu.
Konstruktory w przykładzie 1 wykonują operację otwarcia pliku dyskowego. W takiej
sytuacji programista ma obowiązek zadbać o to aby plik ten został zamknięty przed
zakończeniem programu. Operację tą wykonuje destruktor (~Plik). Destruktor jest
wywoływany automatycznie w sytuacji gdy obiekt jest zwalniany (kończy się czas życia
obiektu - w tym przykładzie kończy się program). Dzięki zastosowaniu destruktora
programista nie musi pamiętać (zdarza się zapomnieć:-) o zamknięciu pliku przed
zakończeniem programu. Destruktor powinien również zwalniać pamięć rezerwowaną
dynamicznie w czasie działania programu, zamykać połączenia sieciowe itp.
Przykład 1. Definicja klasy Plik, utworzenie dwóch plików dyskowych
#include <stdio.h>
class
Plik
{
private
:
FILE* plik;
// zmienna plikowa
Public
:
Plik();
// konstruktor bezparametrowy
Plik(
char
*NazwaPliku);
// konstruktor z parametrem - nazwa pliku
~Plik();
// destruktor
void
zapisz(
char
*line);
// metoda zapisujaca linie tekstu do pliku
};
// konstruktor tworzy plik o nazwie wbudowanej default.txt
Plik::Plik()
{
if
((plik = fopen(
"default.txt"
,
"wt"
)) ==
NULL
)
{
fprintf(stderr,
"Nie moge otworzyc pliku!!!.\n"
);
}
}
Podstawy programowania obiektowego
Strona 3
// konstruktor tworzy plik o nazwie podanej jako parametr
Plik::Plik(
char
*NazwaPliku)
{
if
((plik = fopen(NazwaPliku,
"wt"
)) ==
NULL
)
{
fprintf(stderr,
"Nie moge otworzyc pliku!!!.\n"
);
}
}
void
Plik::zapisz(
char
*text)
{
fprintf(plik, text);
// zapis lini do pliku
fprintf(plik,
"\n"
);
// przejscie do nastepnej linii
printf(
"Zapisano do pliku: %s\n\r"
, text);
}
// destruktor zamyka plik
Plik::~Plik()
{
if
(plik) fclose(plik);
}
void
main()
{
Plik plik1(
"proba.txt"
);
// konstruktor z parametrem - nazwa pliku
Plik plik2;
// konstruktor bezparametrowy - plik default.txt
plik1.zapisz(
"--------- plik proba.txt ------------"
);
// zapis do 1 pliku
plik1.zapisz(
"proba zapisu tekstu"
);
plik1.zapisz(
"---------- koniec zapisu -------------"
);
plik2.zapisz(
"--------- plik default.txt ----------"
);
// zapis do 2 pliku
plik2.zapisz(
"proba zapisu tekstu"
);
plik2.zapisz(
"---------- koniec zapisu -------------"
);
}
2. Hermetyzacja - ukrywanie informacji
Projektant-programista ma możliwość ukrycia najbardziej niepewnych części projektu
i przez to zmniejszenia niebezpieczeństwa ewentualnych przeróbek (zmniejszenia wpływu
na pozostałe moduły projektu). W systemach obiektowych przyjęto formalny podział praw
dostępu do interfejsu (warstwy zewnętrznej):
•
obszar publiczny (public) widzialny dla wszystkich innych elementów projektu,
•
obszar chroniony (protected) widzialny tylko dla podklas danej klasy (niewidzialny
dla innych klas i funkcji),
•
obszar prywatny (private) widzialny dla danej klasy i klas, funkcji
zaprzyjaźnionych.
Możemy dopuścić funkcję do obszaru prywatnego klasy przez deklarację klasy jako funkcję
zaprzyjaźnioną. Funkcja zaprzyjaźniona nie jest funkcją składową klasy. W przykładzie 2
zdefiniowane zostały dwie funkcje zaprzyjaźnione: odejmij i porownaj.
Przykład 2. Definicja klasy zespolona, demonstracja działań na liczbach zespolonych
#include <iostream.h>
// cin, cout
#include <math.h>
// fabs, sqrt
#include <conio.h>
#define TRUE
1
#define FALSE
0
// deklaracja klasy (interfejs klasy)
class
zespolona
{
Podstawy programowania obiektowego ćw nr 2
Strona 4
public
:
zespolona();
// konstruktor
zespolona(
double
r,
double
i =
0
);
// konstruktor
void
ustaw(
double
r,
double
i);
// ustawienie wartosci
void
drukuj();
void
czytaj();
double
modul();
double
rzecz() {
return
re; }
double
uroj() {
return
im; }
zespolona sprzez();
zespolona dodaj(zespolona z);
// funkcje zaprzyjaznione z klasa
friend zespolona odejmij(zespolona z1, zespolona z2);
friend
int
porownaj(zespolona z1, zespolona z2);
// pola prywatne
private
:
double
re, im;
};
// definicja metod
zespolona::zespolona()
{
re =
0
; im =
0
;
}
zespolona::zespolona(
double
r,
double
i)
{
re = r; im = i;
}
void
zespolona::ustaw(
double
r,
double
i)
{
re = r; im = i;
}
void
zespolona::drukuj()
{
cout <<
'('
<< re <<
", "
<< im <<
')'
;
}
void
zespolona::czytaj()
{
cout <<
"re = "
; cin >> re;
cout <<
"im = "
; cin >> im;
}
double
zespolona::modul()
{
return
(sqrt(re*re+im*im));
}
zespolona zespolona::sprzez()
{
return
zespolona(re, -im);
}
zespolona zespolona::dodaj(zespolona z)
{
return
zespolona(re+z.re, im+z.im);
}
// funkcje zaprzyjaznione odejmij i porownaj
zespolona odejmij(zespolona z1, zespolona z2)
{
return
zespolona(z1.re-z2.re, z1.im-z2.im);
}
int
porownaj(zespolona z1, zespolona z2)
{
if
( fabs(z1.re-z2.re) <
1e-10
&& fabs(z1.im-z2.im) <
1e-10
)
return
TRUE;
else
return
FALSE;
}
Podstawy programowania obiektowego
Strona 5
int
main()
{
zespolona z1, z2, z3(
1
), z4(
2
,
3
);
zespolona z5 = z4;
// inicjalizacja
clrscr();
cout <<
"z1 = "
; z1.drukuj(); cout <<
"\tz1"
<< endl;
cout <<
"z2 = "
; z2.drukuj(); cout <<
"\tz2"
<< endl;
cout <<
"z3 = "
; z3.drukuj(); cout <<
"\tz3(1)"
<< endl;
cout <<
"z4 = "
; z4.drukuj(); cout <<
"\tz4(2, 3)"
<< endl;
cout <<
"z5 = "
; z5.drukuj(); cout <<
"\tz5 = z4"
<< endl << endl;
z1.czytaj();
z2.ustaw(
3
, -
4
);
z3 = z1.sprzez();
z4 = z1.dodaj(z2);
z5 = odejmij(z1, z2);
cout <<
"z1 = "
; z1.drukuj(); cout <<
"\tz klawiatury"
<< endl;
cout <<
"z2 = "
; z2.drukuj(); cout <<
"\tustaw(3, -4)"
<< endl;
cout <<
"z3 = "
; z3.drukuj(); cout <<
"\tsprzezona do z1"
<< endl;
cout <<
"z4 = "
; z4.drukuj(); cout <<
"\tz1 + z2"
<< endl;
cout <<
"z5 = "
; z5.drukuj(); cout <<
"\tz1 - z2"
<< endl << endl;
z1 = z2;
// podstawienie
cout <<
"z1 = "
; z1.drukuj(); cout <<
"\tz1 = z2"
<< endl;
cout <<
"Re(z1) = "
<< z1.rzecz() << endl;
cout <<
"Im(z1) = "
<< z1.uroj() << endl;
cout <<
"|z1| = "
<< z1.modul() << endl << endl;
if
( porownaj(z1, z2) == TRUE )
cout <<
"z1 jest rowne z2"
<< endl;
else
cout <<
"z1 jest rozne od z2"
<< endl;
cout << endl <<
"Nacisnij dowolny klawisz..."
;
getch();
return
0
;
}
Zadania do wykonania na zajęciach i w domu:
1. Zdefiniuj klasę pilka. Klasa powinna zawierać:
•
Pole prywatne a typu całkowitego - promień koła.
•
Pole prywatne kolor typu całkowitego - kolor koła.
•
Konstruktor bezparametrowy inicjujący wartości początkowe pól (wybrać dowolne).
•
Konstruktor z parametrami promień i kolor.
•
Metodę publiczną powierzchnia - obliczającą pole powierzchni koła.
•
Metodę publiczną ustawPromien - zmieniającą promien koła.
•
Metodę publiczną ustawKolor - zmieniającą kolor koła.
Zdefiniuj funkcję zaprzyjaźnioną porownaj - porównującą czy dwa koła mają takie same
powierzchnie i kolor.
2. Zdefiniuj klasę prostokat. Klasa powinna zawierać:
•
Pola prywatne a, b typu całkowitego - dlugość boków prostokąta.
•
Konstruktor bezparametrowy inicjujący wartości początkowe pól (wybrać dowolne).
•
Konstruktor z parametrami długość i wysokość prostokąta.
•
Metodę publiczną powierzchnia - obliczającą pole powierzchni prostokąta.
•
Metodę publiczną dajDlugosc - zwracającą długość prostakąta.
•
Metodę publiczną dajWysokosc - zwracającą wysokość prostakąta.
Zdefiniuj funkcję zaprzyjaźnioną jestKwadrat - porównującą boki prostokąta (czy jest kwadratem).
3. Zdefiniuj klasę punkt. Klasa powinna zawierać:
•
Pola prywatne x, y typu całkowitego - współrzędne punktu.
•
Konstruktor bezparametrowy inicjujący wartości początkowe pól (wybrać dowolne).
•
Konstruktor z parametrami: współrzędne punktu.
•
Metodę publiczną dajX - zwracającą wsp. x.
•
Metodę publiczną dajY - zwracającą wsp. y.
Zdefiniuj funkcję zaprzyjaźnioną porównaj - porównującą współrzędne dwóch punktów (czy punkty
się nakładają).
Podstawy
programowania
obiektowego
Ćwiczenia laboratoryjne nr 3
zajęcia zaplanowane na 4 godziny
Temat: Operatory przeciążone, funkcje operatorowe
Prowadzący:
mgr inż. Dariusz Rataj
Koszalin 2001
Podstawy programowania obiektowego ćw nr 2
Strona 2
Spis treści:
1. Przeciążanie operatorów
2. Funkcje operatorowe składowe klasy
3. Funkcje operatorowe zaprzyjaźnione
4. Przykłady
1. Przeciążanie operatorów
Przeciążanie operatora oznacza zdefiniowanie nowego działania operatora dla definiowanej
klasy. W języku C++ mamy możliwość przedefiniowania działania (przeciążenia) prawie
wszystkich operatorów. Wyjątkami są operatory:
. .* ?: :: sizeof
Tworząc nową definicję działania operatora nie zmieniamy jego działania dla typów
standardowych, np.: definiując operator + dla nowotworzonej klasy, działanie tego
operatora dla liczb typu int lub float pozostanie niezmienione. Aby zdefiniować działanie
operatora należy utworzyć funkcję operatorową.
W naszym ćwiczeniu zajmiemy się definicjami podstawowych operatorów dwu
i jednoargumentowych. Funkcje operatorowe możemy zdefiniować jako funkcje składowe
klasy lub jako funkcje zaprzyjaźnione klasy (znane z ćwiczenia nr 2).
2. Funkcje operatorowe składowe klasy
- operatory dwuargumentowe
Operator dwuargumentowy, zdefiniowany jako funkcja składowa klasy, po lewej stronie
zawsze ma argument typu definiowanej klasy. W naszym przykładzie będzie to typ
zespolona.
Przykład:
!
Deklaracja w nagłówku klasy
!
Definicja metody
zespolona zespolona::operator + (zespolona z)
{
zespolona z1(re+z.re, im+z.im);
return z1;
}
zasady stosowania:
Typ zwracany
przez operator
Słowo kluczowe
"operator"
symbol
operatora
Prawy argument
operatora
(typ identyfikator)
zespolona operator + (zespolona z);
Podstawy programowania obiektowego
Strona 3
−
stosujemy wtedy, gdy lewy argument jest tego samego typu co klasa, np. w operatorach
+ , -, *, /, =, = =,
−
definicja metody operatorowej poza klasą posiada identyfikator klasy: zespolona:: , tak
samo jak każda inna metoda klasy.
- operatory jednoargumentowe
!
Deklaracja w nagłówku klasy
zespolona operator * ();
!
Definicja metody
zespolona zespolona::operator * ()
{
return zespolona(re, -im);
}
3. Funkcje operatorowe zaprzyjaźnione
Tak zdefiniowany operator po prawej i lewej stronie ma argumenty dowolnego typu.
Przykład:
!
Deklaracja w nagłówku klasy
!
Definicja metody
ostream& operator << (ostream& o, zespolona z)
{
return o << '(' << z.re << ")+(j" << z.im << ')';
}
zasady stosowania:
−
stosujemy wtedy, gdy lewy argument jest innego typu (może być ten sam typ) co klasa,
np. w operatorach << ,>>. Przeważnie prawy argument jest tego samego typu co
definiowana klasa,
−
deklaracja metody operatorowej w nagłówku klasy posiada słowo kluczowe friend
informujące, że jest to metoda zaprzyjażniona (nie jest metodą składową klasy!). W
definicji metody to słowo nie występuje.
Typ zwracany
przez operator
Słowo kluczowe
"operator"
symbol
operatora
lewy argument
operatora
(typ identyfikator)
prawy argument
operatora
(typ identyfikator)
Słowo kluczowe
"friend" - metoda
zaprzyjaźniona
friend ostream& operator << (ostream& o, zespolona
z);
Podstawy programowania obiektowego ćw nr 2
Strona 4
−
przeważnie typ zwracany przez metodę operatorową jest taki sam jak typ lewego
argumentu (możemy przyjąć to jako zasadę). W naszym przykładzie typ ostream&
(referencja na ostream).
4. Przykłady
Przykład 1. Definicja klasy osoba. Klasa zawiera trzy pola prywatne: nazwisko, imie, pesel typu
tekstowego (tablica znaków), dwie funkcje operatorowe zaprzyjaźnione: operator wyjścia << i
operator wejścia >>.
#include <iostream.h>
// cin, cout, ostream, istream
class
osoba
{
private
:
char
nazwisko[
30
], imie[
20
], pesel[
12
];
public
:
osoba();
// operator wyjscia drukuje dane na konsoli
friend ostream& operator <<(ostream& out, osoba& o);
// operator wejscia pobiera dane z konsoli
friend istream& operator >>(istream& in, osoba& o);
};
osoba::osoba()
{
nazwisko[
0
] =
0
;
// pierwszy znak tablicy = 0 -> tekst pusty
imie[
0
] =
0
;
pesel[
0
] =
0
;
}
ostream& operator << (ostream &out, osoba& o)
// op.wyjscia
{
out << o.nazwisko <<
" "
;
out << o.imie <<
" "
;
out << o.pesel <<
" "
;
return
out;
}
istream& operator >> (istream &in, osoba& o)
// op.wyjscia
{
in >> o.nazwisko >> o.imie >> o.pesel;
return
in;
}
void
main()
{
osoba o;
// deklaracja obiektu osoba
cout <<
"\n podaj nazwisko, imie i pesel\n"
;
cin >> o;
// wprowadzenie danych do obiektu
cout << o;
// wyprowadzenie danych na ekran
}
Podstawy programowania obiektowego
Strona 5
Przykład 2. Definicja klasy Plik umożliwiającą wyprowadzenie zawartości pliku na ekran. Klasa zawiera
jedno pole prywatne plik typu FILE * (struktura opisująca strumień - plik dyskowy), funkcję
operatorową zaprzyjaźnioną definiującą operator wyjścia << .
#include <stdio.h>
// FILE, fopen, fclose, ...
#include <iostream.h>
// cout, cin, ostream
class
Plik
{
private
:
FILE* plik;
public
:
Plik(
char
*NazwaPliku);
// konstruktor otwiera plik dyskowy
~Plik();
// destruktor zamyka plik dyskowy
// operator wyjscia drukuje plik na konsoli
friend ostream& operator <<(ostream& out, Plik & pl);
};
Plik::Plik(
char
*NazwaPliku)
{
if
((plik = fopen(NazwaPliku,
"rt"
)) ==
NULL
)
{
fprintf(stderr,
"Nie moge otworzyc pliku!!!.\n"
);
}
}
Plik::~Plik()
{
if
(plik) fclose(plik);
}
ostream & operator << (ostream &out, Plik &pl)
// op.wyjscia
{
char
ch;
fseek(pl.plik,
0
, SEEK_SET);
// na poczatek pliku
do
{
ch = fgetc(pl.plik);
out << ch;
}
while
(ch != EOF);
return
out;
}
void
main()
{
Plik p(
"autoexec.bat"
);
cout << p;
}
Podstawy programowania obiektowego ćw nr 2
Strona 6
Przykład 3. Definicja klasy zespolona. Przykład ten jest rozszerzeniem przykładu z ćwiczenia nr 2
o szereg operatorów jedno i dwuargumentowych. Funkcje operatorowe zostały zdefiniowane jako
składowe klasy lub jako funkcje zaprzyjaźnione. Przykład do samodzielnej analizy.
#include <iostream.h>
// cin, cout, istream, ostream
#include <math.h>
// fabs, sqrt
#include <conio.h>
// clrscr, getch
typedef enum BOOL { FALSE =
0
, TRUE };
// deklaracja klasy (interfejs klasy)
class
zespolona
{
private
:
double
re, im;
public
:
zespolona() { re =
0
; im =
0
; }
zespolona(
double
r,
double
i =
0
): re(r), im(i) { }
void
ustaw(
double
r,
double
i) { re = r; im = i; }
// przeciazone operatory
zespolona operator * ();
zespolona operator + (zespolona z);
friend zespolona operator - (zespolona z1, zespolona z2);
zespolona& operator += (zespolona z);
friend ostream& operator << (ostream &os, zespolona z);
friend istream& operator >> (istream &is, zespolona &z);
friend BOOL operator == (zespolona z1, zespolona z2);
};
// definicja klasy (implementacja klasy), tzn. definicje funkcji
// skladowych klasy i funkcji zaprzyjaznionych z klasa
zespolona zespolona::operator * ()
{
return
zespolona(re, -im);
}
zespolona zespolona::operator + (zespolona z)
{
return
zespolona(re+z.re, im+z.im);
}
zespolona operator - (zespolona z1, zespolona z2)
{
return
zespolona(z1.re-z2.re, z1.im-z2.im);
}
zespolona& zespolona::operator += (zespolona z)
{
re += z.re; im += z.im;
return
*this;
}
ostream& operator << (ostream &os, zespolona z)
{
return
os <<
'('
<< z.re <<
", "
<< z.im <<
')'
;
}
istream& operator >> (istream &is, zespolona &z)
{
cout <<
"re = "
; is >> z.re;
cout <<
"im = "
; is >> z.im;
return
is;
}
BOOL operator == (zespolona z1, zespolona z2)
Podstawy programowania obiektowego
Strona 7
{
if
( fabs(z1.re-z2.re) <
1e-10
&& fabs(z1.im-z2.im) <
1e-10
)
return
TRUE;
else
return
FALSE;
}
int
main()
{
zespolona z1, z2, z3(
1
), z4(
2
,
3
);
zespolona z5 = z4;
// inicjalizacja
clrscr();
cout <<
"z1 = "
<< z1 <<
"\tz1"
<< endl;
// operator << (cout, z1);
cout <<
"z2 = "
<< z2 <<
"\tz2"
<< endl;
cout <<
"z3 = "
<< z3 <<
"\tz3(1)"
<< endl;
cout <<
"z4 = "
<< z4 <<
"\tz4(2, 3)"
<< endl;
cout <<
"z5 = "
<< z5 <<
"\tz5 = z4"
<< endl << endl;
cout <<
"Podaj z1:"
<< endl;
cin >> z1;
// operator >> (cin, z1);
z2.ustaw(
3
, -
4
);
z3 = *z1;
// z3 = z1.operator * ();
z4 = z1 + z2;
// z4 = z1.operator + (z2);
z5 = z1 - z2;
// z5 = operator - (z1, z2);
cout <<
"z1 = "
<< z1 <<
"\tz klawiatury"
<< endl;
cout <<
"z2 = "
<< z2 <<
"\tustaw(3, -4)"
<< endl;
cout <<
"z3 = "
<< z3 <<
"\tsprzezona do z1"
<< endl;
cout <<
"z4 = "
<< z4 <<
"\t= z1 + z2"
<< endl;
cout <<
"z5 = "
<< z5 <<
"\t= z1 - z2"
<< endl << endl;
z4 = z1 +
2
;
// nie mozna: z4 = 2 + z1;
cout <<
"z4 = "
<< z4 <<
"\t= z1 + 2"
<< endl;
z4 = z1 -
2
;
// mozna: z4 = 2 - z1;
cout <<
"z4 = "
<< z4 <<
"\t= z1 - 2"
<< endl;
z5 = z1 + z2 +
2
-
1
- *z1 + z1 + z2 -
1.5
;
cout <<
"z5 = "
<< z5;
cout <<
"\t= z1 + z2 + 2 - 1 - *z1 + z1 + z2 - 1.5"
<< endl;
z1 = z2;
// podstawienie
cout <<
"z1 = "
<< z1 <<
"\tz1 = z2"
<< endl;
z1 += z2;
// z1.operator += (z2);
cout <<
"z1 = "
<< z1 <<
"\tz1 += z2"
<< endl;
if
(z1 == z2)
// if ( operator == (z1, z2) )
cout <<
"z1 jest rowne z2"
<< endl;
else
cout <<
"z1 jest rozne od z2"
<< endl;
cout << endl <<
"Nacisnij dowolny klawisz..."
;
getch();
return
0
;
}
Podstawy programowania obiektowego ćw nr 2
Strona 8
Zadania do wykonania na zajęciach i w domu:
1. Utwórz klasę wektor - jednowymiarową tablicę wartości typu float. Klasa powinna zawierać:
•
pole prywatne p - wskaźnik na początek tablicy wartości typu float,
•
konstruktor z parametrem typu int - rozmiarem tablicy tworzący dynamicznie tablicę (new) o
odpowiednim rozmiarze,
•
destruktor zwalniający pamięć zarezerwowaną przez konstruktor,
•
funkcję operatorową >>, tak aby można było wprowadzać dane z klawiatury do wektora,
•
funkcję operatorową <<, tak aby można było wyprowadzać dane z wektora na ekran.
2. Utwórz klasę wektor - jednowymiarową tablicę wartości typu int. Klasa powinna zawierać:
•
pole prywatne tab - wskaźnik na początek tablicy wartości typy int,
•
konstruktor z parametrem typu int - rozmiarem tablicy tworzący dynamicznie tablicę (new) o
odpowiednim rozmiarze,
•
destruktor zwalniający pamięć zarezerwowaną przez konstruktor,
•
funkcję operatorową >>, tak aby można było wprowadzać dane z klawiatury do wektora,
•
funkcję operatorową <<, tak aby można było wyprowadzać dane z wektora na ekran.
3. Dla przykładu nr 2 (klasa Plik) rozszerzyć możliwości klasy o funkcje:
•
konstruktor przeciążony tworzący nowy plik dyskowy,
•
funkcje operatorową >>, tak aby można było wprowadzać dane z klawiatury do nowego pliku
dyskowego,
•
funkcję operatorową =, tak aby można było przypisać zawartość pliku,
•
funkcję operatorową +, tak aby można było dodawać zawartości dwóch plików. Wraz z
operatorem = otrzymamy możliwość wykonania działania:
void main()
{
Plik plik1("pl1.txt"), plik2("pl2.txt"), plik1("pl2.txt");
plik1 = plik2 + plik3;
}
4. Dla przykładu nr 2 (klasa Plik) rozszerzyć możliwości klasy o funkcje:
•
konstruktor przeciążony tworzący nowy plik dyskowy,
•
funkcje operatorowe <<, tak aby można było wykonać działanie:
void main()
{
float f = 2.34;
int i = 5;
char tekst[] = "Hallo - to tekst";
Plik plik1("pl1.txt");
plik1 << f;
// zapis do pliku wartości typu
float
plik1 << i; // za
pis do pliku wartości typu
int
plik1 << tekst; // zapis do pliku tekstu z tablicy
}
Uwaga!!! Konieczne jest zdefiniowanie trzech oddzielnych operatorów << dla każdego typu danych.
5. Utwórz klasę okrag - obiekt graficzny. Klasa powinna zawierać:
•
pola prywatne r - promień, x - wsp. x środka, y - wsp. y środka,
•
konstruktor z parametrami: promien, wsp. x i y środka,
•
metodę rysuj rysującą okrag w trybie graficznym systemu DOS,
•
funkcje operatorową ++ przesuwającą okrąg o 1 punkt w prawo,
•
funkcje operatorową -- przesuwającą okrąg o 1 punkt w lewo.
•
funkcje operatorową + dodającą dwa okręgi (działanie dodawania można przyjąć dowolne),
•
funkcje operatorową = umożliwiającą przypisanie,
•
funkcje operatorową == porównującą dwa okręgi.
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ą).