07 - Programowanie obiektowe - klasy, obiekty, ochrona danych.pdf
(
233 KB
)
Pobierz
Lekcja 7: Programowanie obiektowe - klasy, obiekty, ochrona
danych.
Wst
ħ
p
Umiecie ju
Ň
pisa
ę
programy strukturalne (przynajmniej mamy tak
Ģ
nadziej
ħ
). Prawdopodobnie słyszeli
Ļ
cie te
Ň
o obiektach - mo
Ň
e
jeszcze nie w naszym podr
ħ
czniku, ale gdzie
Ļ
na pewno. Aby w pełni skorzysta
ę
z mo
Ň
liwo
Ļ
ci programowania wizualno-obiektowego
w BCB, musicie pozna
ę
cho
ę
by podstawy programowania zorientowanego obiektowo, b
ħ
d
Ģ
cego istot
Ģ
programowania w C++. To
przede wszystkim bezpo
Ļ
rednim wsparciem dla takiego programowania ró
Ň
ni si
ħ
j
ħ
zyk C++ od "zwykłego", czyli strukturalnego j
ħ
zyka
C.
Oprócz zwykłej dawki teorii zamie
Ļ
cili
Ļ
my wi
ħ
c w tej lekcji przykład obiektowej analizy problemu. Natomiast nie b
ħ
dzie tu ju
Ň
kalkulatora. Zamiast niego - zamieszczamy przykład przydatnej klasy, która realizuje rotacyjny bufor na liczby o wysokiej wydajno
Ļ
ci.
Obiekty i klasy
W trakcie dotychczasowego kursu programowania poznali
Ļ
cie (mamy tak
Ģ
nadziej
ħ
) podstawy programowania strukturalnego. Jest to
historycznie najstarsza metodologia programowania. Mimo tego jest do tej pory znana i cz
ħ
sto stosowana, szczególnie wsz
ħ
dzie tam,
gdzie nie mamy dost
ħ
pu do nowoczesnych j
ħ
zyków obiektowych lub gdzie nale
Ň
y szybko rozwi
Ģ
za
ę
niezbyt skomplikowany problem.
Ta lekcja po
Ļ
wi
ħ
cona b
ħ
dzie innemu podej
Ļ
ciu do programowania, tzn.
programowaniu zorientowanemu obiektowo
. Aby pokaza
ę
ró
Ň
nic
ħ
pomi
ħ
dzy obiektowym a strukturalnym podej
Ļ
ciem do programowania, spróbujmy jeszcze raz sprecyzowa
ę
, czym
charakteryzuje si
ħ
programowanie strukturalne. W tego typu podej
Ļ
ciu do programowania najwa
Ň
niejszy jest algorytm, natomiast
sposób zapisu i pami
ħ
tania danych, na jakich operuje, jest drugorz
ħ
dny.
Wady programowania strukturalnego
Analizuj
Ģ
c problem strukturalnie, zawsze układamy pewn
Ģ
kolejno
Ļę
działa
ı
, operacji wykonywanych przez komputer. Efektem tego
jest
powstawanie
funkcji.
Ka
Ň
dy
z
algorytmów
wymaga
pewnych
danych
wej
Ļ
ciowych,
które
przekształca,
produkuj
Ģ
c
dane
wyj
Ļ
ciowe. Tak wi
ħ
c wybór czy te
Ň
opracowanie
algorytmu determinuje, jakich
danych b
ħ
dzie potrzebował i
jak maj
Ģ
by
ę
one
zapami
ħ
tywane. W ten sposób dostajemy list
ħ
parametrów i zmiennych przekazywanych do procedury.
ņ
eby nie by
ę
gołosłownym, przeanalizujmy proces powstawania prostego programiku rysuj
Ģ
cego np. prostok
Ģ
t na ekranie komputera i
dokonuj
Ģ
cego na nim podstawowych przekształce
ı
, tzn. obrotu i przesuni
ħ
cia. Celem jest otrzymanie rysunku i jego pó
Ņ
niejsze
przekształcenia. Najpierw zastanówmy si
ħ
, jak rysuje si
ħ
prostok
Ģ
t. Wystarczy,
Ň
e narysujemy cztery odcinki ł
Ģ
cz
Ģ
ce jego wierzchołki.
Potrzebujemy wi
ħ
c zapami
ħ
ta
ę
poło
Ň
enia czterech punktów stanowi
Ģ
cych wierzchołki prostok
Ģ
ta. Oczywi
Ļ
cie nie jest to jedyny sposób
opisu, jaki mo
Ň
emy zastosowa
ę
, lecz wydaje si
ħ
,
Ň
e jest najbardziej naturalny. Je
Ļ
li chcemy przesun
Ģę
prostok
Ģ
t o wektor (x,y),
wystarczy
Ň
e do odpowiednich współrz
ħ
dnych dodamy współrz
ħ
dne wektora. Inaczej z obrotem - aby go dokona
ę
, musimy zna
ę
Ļ
rodek i k
Ģ
t, o jaki nale
Ň
y prostok
Ģ
t obróci
ę
. W tym przypadku wygodniej byłoby pami
ħ
ta
ę
prostok
Ģ
t jako poło
Ň
enie jego
Ļ
rodka,
długo
Ļę
przek
Ģ
tnej oraz dwa k
Ģ
ty: pomi
ħ
dzy przek
Ģ
tnymi i pomi
ħ
dzy jedn
Ģ
z nich a osi
Ģ
układu współrz
ħ
dnych. W wyniku
przedstawionej analizy otrzymujemy kilka procedur oraz struktur
ħ
danych, w jakiej pami
ħ
tamy prostok
Ģ
t. Przykładowo, mogłoby to
wygl
Ģ
da
ę
tak:
struct
SPr1
{
int
x1, y1, x2, y2, x3, y3, x4, y4;
};
struct
SPr2
{
int
xs, ys;
double
dl_p;
double
kat_p, kat_ox;
};
void
rysuj(SPr1 p);
SPr1 przesun (SPr1 p,
int
x,
int
y);
SPr2 obroc(SPr2 p,
double
kat);
I jeszcze dwie procedury pomocnicze, wykorzystywane przez procedur
ħ
obracaj
Ģ
c
Ģ
, a słu
ŇĢ
ce do przekształcenia opisu opartego na
czterech punktach w opis oparty na
Ļ
rodku, przek
Ģ
tnej i dwu k
Ģ
tach - i na odwrót:
SPr2 wierzcholki_na_katy(SPr1 p);
SPr1 katy_na_wierzcholki(SPr2 p);
Dobrym zwyczajem w programowaniu jest przewidywanie sytuacji bł
ħ
dnych i reagowanie na nie, tak wi
ħ
c ka
Ň
d
Ģ
z wymienionych
procedur nale
Ň
ałoby poszerzy
ę
o
sprawdzanie
bł
ħ
dów (np. czy
dane
cztery punkty rzeczywi
Ļ
cie tworz
Ģ
prostok
Ģ
t, czy
długo
Ļę
przek
Ģ
tnej nie jest ujemna, itp ...).
Je
Ň
eli uwa
Ň
nie przeczytali
Ļ
cie powy
Ň
szy tekst, zauwa
Ň
yli
Ļ
cie zapewne,
Ň
e teraz stosunkowo proste zadanie narysowania prostok
Ģ
ta, po
analizie i rozpisaniu na procedury, nie jest ju
Ň
takie przejrzyste. Musimy pami
ħ
ta
ę
, która procedura potrzebuje jakich danych, mamy
dwa niezale
Ň
ne opisy prostok
Ģ
ta i musimy dba
ę
o synchronizacj
ħ
pomi
ħ
dzy nimi; przy tym zadania wykonywane przez procedury
cz
ħĻ
ciowo si
ħ
pokrywaj
Ģ
(kontrola poprawno
Ļ
ci danych wej
Ļ
ciowych powinna znale
Ņę
si
ħ
w ka
Ň
dej procedurze) itd. Je
Ļ
li chcieliby
Ļ
my
dalej rozbudowywa
ę
ten program o nowe figury, bardzo szybko dostaniemy olbrzymi
Ģ
liczb
ħ
procedur i zmiennych w lu
Ņ
ny sposób
powi
Ģ
zanych ze sob
Ģ
.
Zaprezentowane powy
Ň
ej podej
Ļ
cie do problemu nie jest do ko
ı
ca typowe dla człowieka. Na ogół jak my
Ļ
limy o prostok
Ģ
cie, to
my
Ļ
limy o nim jako o cało
Ļ
ci. Prostok
Ģ
t jest figur
Ģ
geometryczn
Ģ
składaj
Ģ
c
Ģ
si
ħ
z czterech boków parami równoległych, o wszystkich
k
Ģ
tach prostych. Prostok
Ģ
t mo
Ň
na opisa
ę
na kilka sposobów, mo
Ň
na narysowa
ę
, przemie
Ļ
ci
ę
, obróci
ę
, zetrze
ę
. W sposób naturalny
traktujemy prostok
Ģ
t jako obiekt. Podobnie traktujemy np. samochód - blaszane pudełko na kółkach, okre
Ļ
lone kolorem, mark
Ģ
,
kształtem, które przemieszcza si
ħ
, skr
ħ
ca, ma pewne osi
Ģ
gi itd... Powstaje wi
ħ
c pytanie - dlaczego takiego wła
Ļ
nie postrzegania
Ļ
wiata
nie przenie
Ļę
do programowania? Odpowiedzi
Ģ
na nie jest wła
Ļ
nie
programowanie zorientowane obiektowo
.
Co to jest obiekt i czym jest klasa
Skoro mówimy o programowaniu zorientowanym obiektowo, potrzebna jest jaka
Ļ
definicja obiektu. Podczas tego kursu
obiektem
b
ħ
dziemy nazywali dane i algorytmy na nich operuj
Ģ
ce. Zatem w powy
Ň
szym przykładzie w skład obiektu nazywanego prostok
Ģ
tem
b
ħ
d
Ģ
wchodzi
ę
nie tylko dane stanowi
Ģ
ce jego opis, ale i wszystkie procedury słu
ŇĢ
ce zarówno do zmiany czy przekształcania tych
danych, czy te
Ň
do wykonywania odpowiednich działa
ı ŇĢ
danych przez u
Ň
ytkownika (np. narysowania obiektu na ekranie). W dalszym
opisie dane b
ħ
dziemy cz
ħ
sto
nazywali
polami
lub
wła
Ļ
ciwo
Ļ
ciami obiektu, natomiast algorytmy zapisane w postaci funkcji lub
procedur -
metodami
.
Na pocz
Ģ
tek mo
Ň
ecie sobie wyobrazi
ę
obiekty jako troch
ħ
bardziej zło
Ň
one rekordy, które poznali
Ļ
cie ju
Ň
wcze
Ļ
niej. Oprócz pól, do
których ju
Ň
jeste
Ļ
my przyzwyczajeni, dokładamy jeszcze metody, definiowane w sposób zbli
Ň
ony do definiowania funkcji.
rekordy składaj
Ģ
si
ħ
z pól
obiekty składaj
Ģ
si
ħ
z pól i metod działaj
Ģ
cych na tych polach
I podobnie jak definiowali
Ļ
my
typ rekordowy
, by opisa
ę
struktur
ħ
rekordu, mo
Ň
emy teraz zdefiniowa
ę
typ obiektowy
, by opisa
ę
struktur
ħ
obiektu. Przy czym zamiast poj
ħ
cia typu obiektowego w C++ u
Ň
ywa si
ħ
poj
ħ
cia
klasa
(klasa obiektów).
Zamiast mówi
ę
typ obiektowy
mo
Ň
emy mówi
ę
po prostu:
klasa
.
Definicja klasy jest podobna do definicji typu rekordowego (struktur), musi tylko uwzgl
ħ
dnia
ę
metody. W opisie typu rekordowego
u
Ň
ywali
Ļ
my słowa kluczowego
struct
. W opisie typu obiektowego (klasy) b
ħ
dziemy u
Ň
ywa
ę
słowa kluczowego
class
.
W przypadku najprostszych klas, w których nie jest potrzebne definiowanie widoczno
Ļ
ci poszczególnych pól czy te
Ň
metod,
mo
Ň
emy pozosta
ę
przy słowie
struct
lub u
Ň
ywa
ę
go zamiennie ze słowem
class
, lecz lepiej od razu przyzwyczaja
ę
si
ħ
do dobrego
standardu u
Ň
ywania słowa
class
, którego wymaga prawdziwe programowanie obiektowe.
Załó
Ň
my,
Ň
e chcemy zdefiniowac klas
ħ
b
ħ
d
Ģ
c
Ģ
bardzo prost
Ģ
komputerow
Ģ
reprezentacj
ħ
diody. Przyjmijmy,
Ň
e dioda, traktowana jako
ogólne poj
ħ
cie, b
ħ
dzie okre
Ļ
lona przez dwie wła
Ļ
ciwo
Ļ
ci (dwa pola):
ma jaki
Ļ
kolor (pole typu string)
jest zapalona albo nie (pole typu bool)
Na polach tych chcemy móc wykonywa
ę
nast
ħ
puj
Ģ
ce operacje (metody):
"zapalenie diody"
"zgaszenie" diody
obejrzenie jej stanu: czy
Ļ
wieci i w jakim kolorze.
Nasz
Ģ
klas
ħ
opisuj
Ģ
c
Ģ
poj
ħ
cie diody nazwiemy CDioda - zaczynaj
Ģ
c jej nazw
ħ
od du
Ň
ego C, by od razu było wida
ę
,
Ň
e jest to nazwa
klasy. To nieobowi
Ģ
zuj
Ģ
ca, ale wygodna konwencja, analogiczna do zapisu struktur - typów rekordowych, których nazwy zaczynali
Ļ
my
od du
Ň
ego S (lub T).
Mo
Ň
emy wi
ħ
c ju
Ň
zaproponowa
ę
definicj
ħ
klasy CDioda:
class
CDioda {
public
:
string kolor;
bool
zapalona;
void
zapal() {
zapalona =
true
;
}
void
zgas() {
zapalona =
false
;
}
void
pokaz() {
if
(zapalona)
cout <<
"Swieci w kolorze "
<< kolor << endl;
else
cout <<
"Nie swieci\n"
;
}
};
W ten sposób pokazali
Ļ
my Wam, jak mo
Ň
na zdefiniowa
ę
nowy typ obiektowy - klas
ħ
. Lecz typ obiektowy, czyli klasa obiektów, albo
krócej klasa, to jeszcze nie obiekt - pami
ħ
tajcie o tym. Definicja klasy jest opisem wspólnych metod i pól, specyficznych dla
wszystkich obiektów tej klasy, bez podawania ich konkretnych warto
Ļ
ci. Przykładowo klasa obiektów znana pod poj
ħ
ciem człowiek
definiuje, i
Ň
ka
Ň
dy człowiek ma kolor oczu. Nie jest powiedziane jaki - wiadomo tylko,
Ň
e ma. Natomiast doprecyzowanie (okre
Ļ
lenie,
jaki to jest kolor) - odbywa si
ħ
w odniesieniu do konkretnego obiektu - takiego lub innego człowieka. Podobnie
klasa CDioda
definiuje,
Ň
e ka
Ň
dy obiekt tej klasy (dioda) ma jaki
Ļ
kolor. A jaki - to ju
Ň
zale
Ň
y od konkretnej diody.
Tutaj mamy wi
ħ
c podobn
Ģ
sytuacj
ħ
jak w przypadku rekordów. Najpierw definiowali
Ļ
my typ rekordowy (struktur
ħ
), a dopiero potem
rekordy - zmienne danego typu. I dopiero z tych zmiennych mogli
Ļ
my zacz
Ģę
korzysta
ę
. Podobnie wygl
Ģ
da sytuacja w przypadku
obiektów.
ņ
eby mo
Ň
na było ró
Ň
ne obiekty - diody wykorzysta
ę
w naszym programie, musimy najpierw zdefiniowa
ę
klas
ħ
CDioda, a
potem utworzy
ę
jakie
Ļ
zmienne klasy CDioda (zmienne typu obiektowego CDioda).
Zobaczcie to na przykładzie:
1.
#include <iostream>
2.
#include <cstdlib>
3.
4.
using
namespace
std;
5.
6.
/* Definicja typu obiektowego - klasy CDioda*/
7.
class
CDioda {
8.
public
:
9.
// definicje pól o nazwach kolor i zapalona
10.
string kolor;
11.
bool
zapalona;
12.
// definicja metody zapal
13.
void
zapal() {
14.
zapalona =
true
;
15.
}
16.
// definicja metody zgas
17.
void
zgas() {
18.
zapalona =
false
;
19.
}
20.
// definicja metody pokaz
21.
void
pokaz() {
22.
if
(zapalona)
23.
cout <<
"Swieci w kolorze "
<< kolor << endl;
24.
else
25.
cout <<
"Nie swieci\n"
;
26.
}
27.
};
28.
29.
int
main(
int
argc,
char
*argv[])
30.
{
31.
cout <<
"Diody ..."
<< endl;
32.
33.
// utworzenie dwóch obiektów typu CDdioda, o nazwach d1 i d2
34.
CDioda d1, d2;
35.
36.
// ustawienie warto
Ļ
ci ich pól
37.
d1.kolor =
"zielony"
;
38.
d2.kolor =
"czerwony"
;
39.
d1.zapalona =
false
;
40.
d2.zapalona =
false
;
41.
42.
// a teraz główna p
ħ
tla progamu - b
ħ
dzie prosi
ę
u
Ň
ytkownika
43.
// o podanie działania - i po ka
Ň
dym poleceniu wy
Ļ
wietla
ę
44.
// stan diod
45.
char
zn;
46.
do
{
47.
cout <<
"Stan diod:\n"
;
48.
d1.pokaz();
49.
d2.pokaz();
50.
cout <<
"\nCo chcesz zrobic?\n1 - zapal diode 1\n"
;
51.
cout <<
"2 - zgas diode 1\n"
;
52.
cout <<
"3 - zapal diode 2\n"
;
53.
cout <<
"4 - zgas diode 2\n"
;
54.
cout <<
"0 - zakoncz program\n"
;
55.
cin >> zn;
56.
switch
(zn) {
57.
case
'1'
: d1.zapal();
break
;
58.
case
'2'
: d1.zgas();
break
;
59.
case
'3'
: d2.zapal();
break
;
60.
case
'4'
: d2.zgas();
break
;
61.
};
62.
}
while
(zn !=
'0'
);
63.
64.
return
0;
65.
}
No tak ... uwa
Ň
ny czytelnik mo
Ň
e stwierdzi
ę
- tyle pisania, tyle teorii, tyle hałasu, a jedyny zysk to fakt,
Ň
e zamiast pisa
ę
pokaz(d1)
piszemy
d1.pokaz()
. I b
ħ
dzie miał racj
ħ
- jak na razie ... przynajmniej dopóki nie poka
Ň
emy mo
Ň
liwo
Ļ
ci ochrony pól i metod w
obiekcie. A prawdziw
Ģ
pot
ħ
g
ħ
programowania
obiekowego zobaczycie dopiero w nast
ħ
pnej lekcji - jak omówimy zupełnie nowe
poj
ħ
cia - dziedziczenie i polimorfizm.
Troch
ħ
składni
Po tym prostym przykładzie z diod
Ģ
mo
Ň
emy ju
Ň
poda
ę
uproszczon
Ģ
, ogóln
Ģ
posta
ę
deklaracji klasy:
class
nazwa_klasy
{
public
: // co znaczy public - w nast
ħ
pnym segmencie
// najpierw definiujemy pola ró
Ň
nych typów
typ_pola
nazwa_pola, ...nazwa_pola
;
...
// potem metody (z parametrami lub bez) operuj
Ģ
ce na tych polach
typ_zwracany
nazwa_metody
(
lista_parametrów
);
...
// potem znowu mog
Ģ
by
ę
inne pola
typ_pola
nazwa_pola, ...nazwa_pola
;
...
// i inne metody
typ_zwracany
nazwa_metody
(
lista_parametrów
);
// i tak dalej...
};
Wyst
ħ
puj
Ģ
cym tutaj słowem kluczowym
public
na razie si
ħ
nie przejmujcie - tylko przyjmijcie,
Ň
e by
ę
musi. Co ono znaczy, i dlaczego
by
ę
musi - wyja
Ļ
nimy w dalszej cz
ħĻ
ci tej lekcji.
W C++, mimo
Ň
e mo
Ň
liwa jest jednoczesna definicja i deklaracja klasy (tak zrobili
Ļ
my w przykładzie wprowadzaj
Ģ
cym - diody) -
zwykle post
ħ
puje si
ħ
inaczej. Ka
Ň
d
Ģ
klas
ħ
rozbija si
ħ
na dwa pliki:
plik z deklaracj
Ģ
(a nie definicj
Ģ
) klasy
plik zawieraj
Ģ
cy implementacj
ħ
klasy, czyli definicje jej metod.
Klas
ħ
deklaruje si
ħ
w pliku nagłówka (*.h), natomiast definicje metod wchodz
Ģ
cych w jej skład umieszcza w implementacji (*.cpp).
Tak wi
ħ
c klasa CDioda po poprawkach powinna wygl
Ģ
da
ę
nast
ħ
puj
Ģ
co: najpierw tworzymy nowy moduł, nast
ħ
pnie w jego pliku
nagłówkowym umieszczamy deklaracj
ħ
klasy:
1.
#ifndef cdiodaH
2.
#define cdiodaH
3.
4.
#include <string>
5.
using
namespace
std;
6.
7.
/* Deklaracja klasy CDioda*/
8.
class
CDioda {
9.
public
:
10.
// pole pami
ħ
taj
Ģ
ce stan diody
11.
bool
zapalona;
12.
// kolor diody
13.
string kolor;
14.
// informacja,
Ň
e dioda ma metod
ħ
zapal
15.
void
zapal();
16.
// informacja,
Ň
e dioda ma metod
ħ
zgas
17.
void
zgas();
18.
// informacja,
Ň
e dioda ma metod
ħ
pokaz
19.
void
pokaz();
20.
};
21.
22.
#endif
W pliku z implementacj
Ģ
podajemy natomiast tre
Ļę
(definicj
ħ
) metod - to tam piszemy, co nale
Ň
y zrobi
ę
. Kierujemy si
ħ
przy tym
zasadami takimi samymi, jak w przypadku tworzenia klasycznych funkcji, z dwoma istotnymi ró
Ň
nicami:
1.
Nazwa metody podczas implementacji składa si
ħ
z dwóch cz
ħĻ
ci. Implementacje (definicje) kolejnych metod tworz
Ģ
tzw.
wn
ħ
trze obiektu
, bo nale
ŇĢ
w cało
Ļ
ci do obiektów danej klasy. W implementacji metody konieczne jest wi
ħ
c zastosowanie
desygnatora
(oznacznika), okre
Ļ
laj
Ģ
cego, do jakiej klasy nale
Ň
y dana metoda. Za
desygnatorem umieszcza si
ħ
podwójny
dwukropek, a dopiero po nim nazw
ħ
metody:
typ_zwracany
nazwa_klasy
::
nazwa_metody
(
lista_parametrów
)
{
...
};
2.
Pisz
Ģ
c ciało (tre
Ļę
) funkcji b
ħ
d
Ģ
cej metod
Ģ
jakiej
Ļ
klasy, mo
Ň
emy odwoływa
ę
si
ħ
do pól tej klasy bezpo
Ļ
rednio, nie podaj
Ģ
c
nazwy klasy, do której dane pole nale
Ň
y - bo przecie
Ň
wewn
Ģ
trz klasy wszystkie pola s
Ģ
znane i bezpo
Ļ
rednio dost
ħ
pne (to tak
jak my - na polecenie "rusz swoj
Ģ
r
ħ
k
Ģ
" - wiemy, która r
ħ
ka jest nasza). Inaczej mówi
Ģ
c - wewn
Ģ
trz metody wszystkie pola klasy
mo
Ň
na traktowa
ę
jak zdefiniowane zmienne lokalne.
Przyjrzyjmy si
ħ
wi
ħ
c przykładowej implementacji:
1.
#include <iostream>
2.
#include <cstdlib>
3.
4.
#include "cdioda.h"
5.
Plik z chomika:
pmtg
Inne pliki z tego folderu:
01 - Podstawowe instrukcje, typy danych i struktura programu.pdf
(966 KB)
02 - Wyrażenia i zaawansowane podejmowanie decyzji. Pętle I.pdf
(690 KB)
03 - Pętle II. Tablice. Pliki tekstowe.pdf
(934 KB)
04 - Rekordy. Definiowanie typów.pdf
(235 KB)
05 - Podprogramy - definicje i wywołania. Debugger.pdf
(830 KB)
Inne foldery tego chomika:
Bazy danych - techniki zaawansowane
Diagnostyka procesów przemysłowych
Elektrohydraulika
Grafika komputerowa
Integracja systemów
Zgłoś jeśli
naruszono regulamin