background image

Programowanie obiektowe

Jebał mnie pies Andrzej Walczak

awalczak@wat.edu.pl

WAT

Pierwsza edycja 2006

Temat: ogólny opis techniki obiektowej

background image

Uwagi wprowadzające

• Jest nowy syllabus przedmiotu. 

Studenci powinni się z nim zapoznać.

• Literatura podstawowa: 

wazniak.mimuw.edu.pl; Bruce Eckel – 
Thinking in C++; Bruce Eckel – 
Thinking in Java; Nicolai Josuttis – C+
+ Biblioteka Standardowa 

background image

Ogólne cechy obiektowej techniki programowania

Zarządzanie informacją w programie – 

współdzielenie danych

 

Większość  wysiłku  usprawniającego  pisanie  dużych 
programów koncentrujemy na dzieleniu procedur (etapów 
integralnych  wykonania  kodu)  na  moduły.  Istnieje  jednak 
inny składnik programu nie mniej ważny niż kod napisany 
w  wybranym  języku.  Tym  składnikiem  są  dane  czyli 
zestaw  informacji,  na  których  funkcjonują  procedury 
programu.

background image

Ogólne cechy obiektowej techniki programowania

Zarządzanie informacją w programie – 

współdzielenie 

danych

Jeśli  program  do  swojego  działania  potrzebuje  tylko  kilka 
kawałków danych, to bez utraty bezpieczeństwa te kawałki 
mogą  być  udostępniane  wszystkim  podprogramom 
tworzącym kod. 
Taka  organizacja  danych  jest  wygodna  dla  programistów 
ponieważ  współdzielony  zestaw  danych  jest  czymś  w 
rodzaju tablicy ogłoszeniowej na której podprogramy mogą 
wymieniać  się  informacjami.  Jeśli  jednak  tych  kawałków 
danych i podprogramów są tysiące takie rozwiązanie często 
prowadzi do nieprzewidywalnych zachowań programu.

background image

Ogólne cechy obiektowej techniki programowania

Zarządzanie informacją w programie – 

współdzielenie danych

Problem polega na tym, że współdzielenie danych 
jest 

pogwałceniem 

modułowej 

konstrukcji 

programowania,  której  podstawą  jest  całkowita 
niezależność 

modułów. 

Dopuszczając 

swobodne  współdziałanie  modułów  poprzez 
wymianę 

informacji 

powodujemy, 

że 

zachowanie  jednego  modułu  wpływa  na 
zachowanie wszystkich pozostałych
.

 

background image

Ogólne cechy obiektowej techniki programowania

• Rozwiązaniem jest 

podzielenie danych

 na części odpowiadające 

modułom programu. 

Podprogramy otrzymują dane, którymi 

tylko

 

one mogą się posługiwać

. Taki zabieg nazywa się przesłanianiem 

danych. 

Wiele  programów  musi  korzystać  z  tych  samych  danych 

wielokrotnie  np.  obliczenia  inżynierskie,  programy  księgowe  etc. 

Często  z  programów  tego  rodzaju  korzysta  jednocześnie  wiele 

osób.  Zawsze  istnieje  możliwość,  że  jedna  z  nich  zmieni  dane, 

któreych inne osoby właśnie używają. 

• Dlatego utworzono tak zwane systemy dostępowe rozbudowane 

aktualnie do Systemów Zarządzania Bazami Danych (Data Base 

Management System). Kierują one wielodostępem do danych i 

chronią je przed zmianami. 

Okazało się , że idea DBMS jest niezwykle skuteczna w tworzeniu 

kodu o podniesionej niezawodności. Ma jednak ten mechanizm 

szereg ograniczeń . Kłopoty ze współdzieleniem danych 

doprowadziły do utworzenia tak zwanego programowania 

obiektowego.

background image

Ogólne cechy obiektowej techniki programowania

• Pierwszy  język  obiektowy  –  Simula  67  –  powstał  już  w  latach 

sześćdziesiątych  ubiegłego  stulecia.  Jego  twórcami  byli  Ole-Johan 

Dahl  i  Kristen  Nygaard  z  Norsk  Regnesentral  w  Oslo.  Podczas 

swoich  prac  nad  symulacją  statków  musieli  dla  każdego  rodzaju 

statku 

uwzględniać 

wiele 

atrybutów. 

Ponieważ 

liczba 

modelowanych  rodzajów  statków  była  duża,  uwzględnienie 

wszystkich  możliwych  zależności  między  atrybutami  stało  się 

problematyczne.  Pojawił  się  pomysł,  aby  pogrupować  różne 

rodzaje  statków  w  klasy  obiektów.  Każda  klasa  obiektów  sama 

miała  być  odpowiedzialna  za  definiowanie  swoich  danych  i 

zachowania.  Simula  była  pierwszym  językiem  programowania,  w 

którym  wprowadzono  pojęcie  klasy  i  jej  egzemplarza.  Warto  tu 

zwrócić  uwagę,  że  zgodnie  z  nazwą  języka  takie  odwzorowanie 

obiektów  spotykanych  w  świecie  rzeczywistym  na  obiekty 

programowe  można  nazwać  symulacją.  Niedługo  potem  w 

laboratorium  badawczym  Xerox's  Palo  Alto  stworzono  Smalltalk. 

Jego  głównym  pomysłodawcą  był  Alan  Kay.  Smalltalk  zawierał 

wiele  rewolucyjnych  pomysłów  -  m.in.  dziedziczenie  -  i  zyskał 

sobie  sporą  popularność.  W  pewnej  skali  był  również  z 

powodzeniem  stosowany  w  praktyce.  Standardem  przemysłowym 

programowanie  obiektowe  stało  się  jednak  dopiero  w  latach 

dziewięćdziesiątych za sprawą języka C++, który jest obiektowym 

rozszerzeniem  C.  Obecnie  jednym  z  najpopularniejszych  języków 

obiektowych  i  równocześnie  języków  programowania  w  ogóle  jest 

Java. *

*wazniak.mimuw.pl

background image

Trzy (albo dwa) klucze do techniki obiektowej: 

obiekty, klasy, komunikaty

 

Jedną  z  barier  w  zrozumieniu  techniki  obiektowej 
jest specjalistyczne słownictwo, którym ta technika 
obrosła. 
W rzeczywistości można tym żargonem posługiwać 
się skutecznie stosując zaledwie kilka pojęć: 

1. obiekt, instancja (konkret)

2. metoda, (funkcja składowa klasy)
3. komunikat, [ 
pojęcie od dawna zaniedbane ]
4. klasa, 
5. podklasa, 

6. dziedziczenie, 
7. hermetyzacja (enkapsulacja), 
8. abstrahowanie,
9.  polimorfizm

background image

O obiektach

Koncepcje  programowania  obiektowego  zostały  pokazane  w  języku 
Simula.  Zbudowano  go  do  symulacji  złożonych  systemów  i  jako 
podstawę konstrukcji języka przyjęto, że 

każdy obiekt rzeczywisty 

charakteryzują jego zachowania

Spróbowano więc tak układać komunikacje pomiędzy 

procedurami i danymi aby były podporządkowane 

zachowaniom symulowanych obiektów

.

Obiekt  był  „pakunkiem”  zawierającym  procedury  i  dane  powiązane   
razem  tak,  aby  określały  jego  możliwe  zachowania.  Procedury  w 
podejściu  obiektowym  nazywa  się  metodami.  W  odniesieniu  do 
danych stosujemy nazwę zmienne bo ich wartość może się zmieniać 
w czasie. Nazywamy metody i zmienne atrybutami obiektu.

background image

Rozważmy dla ilustracji jak moglibyśmy programować 

przenośnik 

fabryczny

. Może on wykonywać rozmaite czynności jak 

przemieszczanie się, ładowanie, rozładowanie. Musi przechowywać 
informacje
 o nośności, rozmiarach palety, prędkości, aktualnego 
położenia, aktualnego ładunku,  itp.  

Tak więc realny obiekt „coś robi” 

i „coś wie”.

Czynności obiektu opiszemy jako metody, a wielkości 
charakterystyczne, którymi obiekt się posługuje 
wykonując czynności, jako zmienne

CZYNNOŚCI OBIEKTU = METODY

DANE DO METOD=ZMIENNE OBIEKTU

Obiekt jest idealnym modułem programu. Tworzy własny, 
zamknięty  świat.  Wszystko  co  „wie”  wyrażają  jego 
zmienne. Wszystko co może robić wyrażają jego metody.

background image

Klasy obiektów

Wykład 2

background image

Pojęcie klasy obiektów

Klasa jest nowym typem zmiennej w programie . Definiujemy ją 

jako:

 
class nasza_klasa {
//
//
...
//ciało klasy
};
 
Jeśli chcemy stworzyć konkretny element czyli obiekt tej klasy to 

zapisujemy :

 
nasza_klasa  

nasz_obiekt

;

 

Wtedy w pamięci operacyjnej 

powstanie 

obiekt klasy 

nasza_klasa, który się nazywa nasz_obiekt

background image

Kiedy już mamy typ  nasza_klasa, to możemy utworzyć obiekt pochodny, na 
przykład wskaźnik do obiektu z naszej_klasy:
 

nasza_klasa *wsk;

(czy mogę określić referencję na nazwę klasy?) 

albo:
 

nasza_klasa &name = obiekcik;

 

Utworzy to wskaźnik do obiektów klasy nasza_klasa albo referencje do 
wybranego obiektu klasy nasza_klasa.
 
Przykładprogram w C++, który przechowuje informację o kolorze i wartości 
punktowej karty do gry. Wykorzystuje klasę Karta
. W kodzie ponumerowano 
wiersze do dalszej analizy.

background image

Kod programu do przykładu

1. #include <iostream>
2. #include <conio.h>
3. #include <string.h>
4. #include <dos.h>
5. class Karta
6. {
7. public:
8. char kol[80];
9. int wym;
10. Karta(char*, int);
11. void Druk();
12. }; 

background image

void main()
{

Karta k1(“czarna”,5),k2(“czerwona”,8); // 

powolanie do zycia 

obiektow k1 oraz k2 klasy Karta

k1.Druk(); 

// 

wykonanie funkcji Druk() na rzecz obiektu k1

k2.Druk();

strcpy(k1.kol,”czarno-czerwona”);  //

zmiana zmiennej kol na rzecz 

obiektu k1

k2.wym=28;

//

zmiana zmiennej wym na rzecz obiektu k2

cout<<endl;

k1.Druk();
k2.Druk();
getch();

background image

Karta::Karta(char* s,int w): wym(w)
{
strcpy(kol,s);
}

 

Definicja 
konstruktora

Inicjalizacja 
argumentu 
konstruktora

Definicja 
funkcji 
składowej klasy

void Karta::Druk()
{
cout<<”\nkolor=”<<kol<<”,wymiar=”<<wym;
}

background image

omówienie:

uwagi o strukturze kodu programu: 

• opis klasy Karta znajduje się przed blokiem main() czyli w tej części 

kodu programu, która przeznaczona jest do umieszczania deklaracji, 
definicji  i  inicjalizowania  typów  zmiennych.  To  także  wskazuje,  że 

klasa jest typem zmiennej

• Opis klasy zawsze umieszczamy w nawiasie klamrowym, po którym 

jest średnik, tak, jak w normalnych deklaracjach typów zmiennej.

• Konstruktor obiektu Karta, o ZAWSZE nazwie takiej samej jak klasa 

jest w obszarze opisu klasy tylko deklarowany i podawana jest lista 
jego argumentów. Nie podajemy typu  pomimo, że jest funkcją.

• Ciało konstruktora, który jest funkcją,  jest opisywane 

poza

 blokiem 

main() jeśli konstruktor jest podany jawnie

• W  bloku  definiowania  klasy  deklarowane  są  także  funkcje 

składowe  klasy,  które  dalej  będą  metodami  obiektów  klasy.  W 
przykładzie jest to funkcja Druk().

• Opis  funkcji  klasy  umieszczamy  po  bloku  main()  tak,  jak  opis 

konstruktora ale może być podany także wewnątrz bloku deklaracji 
klasy.

background image

Opis kodu: 

Wiersze 5-11: początek i koniec opisu klasy Karta zawierający 
deklaracje konstruktora Karta i funkcji Druk() oraz wykorzystywanych 
zmiennych, tablicy char[80] oraz zmiennej całkowitej wym

class Karta

{

public:

char kol[80];

int wym;

Karta(char*, int);

void Druk();

}; 

background image

Wiersze 12-25: blok main(), w którym powołane są dwa obiekty klasy Karta o 
nazwach k1 i k2 w wierszu 14ym. Obiekty mają argumenty takie, jakie 
zadeklarowano w konstruktorze
, czyli zmienna typu char oraz zmienna 
integer. WNIOSEK: deklaracja konstruktora musi zawierać to, co potem 
jest potrzebne w funkcjonowaniu obiektów konstruowanych w klasie

W każdym obiekcie k1 oraz k2 wartości zmiennych deklarowanych są 
zainicjowane poprzez podanie konkretnych wartości. 

void main()
{

Karta 
k1(“czarna”,5),k2(“czerwona”,8);

k1.Druk();
k2.Druk();
strcpy(k1.kol,”czarno-czerwona”);
k2.wym=28;
cout<<endl;

k1.Druk();
k2.Druk();
getch();} 

background image

Wiersze  26-33:  definicja  konstruktora  klasy.  Najpierw  nazwa  Karta,  a 
potem  operator  ::  czyli  operator  zakresu.  Teraz  już  na  liście 
argumentów formalnych  podane  są  nazwy argumentów  s oraz w.  Po 
liście  argumentów  formalnych  może  (  ale  nie  musi)  pojawić  się 
dwukropek i lista inicjalizująca wartości argumentów, czyli argumenty 
początkowe.  Następnie  w  nawiasie  klamrowym,  po  którym  nie  ma 
średnika  umieszczamy  ciało  konstruktora.  W  ciele  konstruktora,  w 
wierszu 27 mamy kopiowanie łańcucha nazwy koloru karty z s do kol.

Karta::Karta(char* s,int w): wym(w)
{

strcpy(kol,s);

background image

Wiersze 34-36: definicja funkcji własnej klasy Karta o nazwie Druk(). Jest ona 
wykonywana w bloku main() na rzecz obiektów k1 oraz k2. To, że jest ona 
funkcją własną klasy Karta wskazuje nazwa klasy rozpoczynająca definicję i 
operator zakresu :: , który wskazuje, że funkcja działa w całym zakresie 
ważności klasy. Funkcja Druk() wykonuje na ekranie wypisanie nazwy koloru 
karty i jej wartości punktowej. 

void Karta::Druk()
{
cout<<”\nkolor=”<<kol<<”,wymiar=”<<wym;

k1.Druk();
k2.Druk();

background image

Kolor obiektu k1 jest modyfikowany w wierszu 18 funkcją strcpy. W 
składni wymienia się nazwę obiektu, a po kropce nazwę argumentu 
zmienianego. W wierszu 19 modyfikowany jest argument wym obiektu 
k2 poprzez zwykłą operację przypisania nowej wartości. 

k1.Druk();
k2.Druk();
strcpy(k1.kol,”czarno-czerwona”);
k2.wym=28;

WNIOSEK: 

Klasa a obiekt

Widać, że 

klasa nie definiuje konkretnych 

obiektów tylko ich typy

!!! Jest ona typem 

obiektu jako abstrakcyjnej zmiennej, a nie 
obiektem lub zbiorem obiektów

background image

Uwagi ogólne do konstruktorów:

1. Konstruktor 

NIE MUSI

 wystąpić w opisie klasy, czyli obiekty nie muszą być 

wprowadzane konstruktorem.

2. Nazwa konstruktora może być przeładowana

, czyli stosowana 

wielokrotnie w opisie klasy z różnymi listami argumentów. Wtedy 
kompilator odróżnia konstruktory po listach argumentów, tak, jak w 
przypadku przeładowanych nazw funkcji. Konstruktorów może wiec być 
wiele.

3. Konstruktor 

może być wywoływany ( a nie deklarowany!!) bez żadnych 

argumentów

. Jest to tak zwany konstruktor domniemany. Czasem 

nazywamy go domyślnym albo standardowym. Ze względu na istotę 
przeładowania nazwy konstruktor domniemany czyli bezargumentowy 
może wystąpić tylko raz. Jeśli nie deklarujemy w klasie żadnego 
konstruktora, to kompilator sam ustanawia właśnie konstruktor 
domniemany do obsługi obiektów w programie. Każdy konstruktor z 
argumentami, którym nadamy wartości domyślne czyli niedefiniowalne 
jest także konstruktorem domniemanym.

4. Co właściwie robi konstruktor? On INICJALIZUJE obiekty. Kompilator 

automatycznie wywołuje konstruktor w miejscu tworzenia obiektu zanim 
jeszcze obiekt podejmie jakiekolwiek działanie. Czyli po pierwszym 
komunikacie staruje konstruktor. Nazwa konstruktora taka sama jak 
nazwa klasy (pomysł Stroustrupa) pozwala na jednoznaczne powiązanie 
konstruktora z typem zmiennej obiektowej (wykorzystano operator 
zakresu).

background image

Konstruktor jest zwykle deklarowany jako 
publiczny, bo przecież wprowadzane nim 
obiekty mogą być używane przez klasy 
zewnętrzne. Możemy jednak dla 
konstruktora przewidzieć ochronę tak, jak 
dla klas za pomocą etykiet private lub 
protected. Wówczas jednak także 
konstruowane obiekty będą dostępne tylko 
w obrębie klasy z tym konstruktorem jako 
private albo jako protected tylko w zakresie 
klas dziedziczących.

Konstruktor może zamiast definiować 
obiekty podawać kopie obiektów zawartych 
w innej klasie lub tworzyc kopie obiektów 
istniejących. Wtedy jest to tak zwany 

konstruktor kopiujący

.

Konstruktor może dokonywać konwersji typu 
obiekty z jednego w drugi. Nazywamy go 
wtedy 

konstruktorem konwertującym

.

background image

Przykład: Konstruktor domniemany 

#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
class X
{
public:
char kol[80];
int wym;

X(char* s=“?”, int w=-1);

void Druk();
}; 

#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
class Karta
{
public:
char kol[80];
int wym;

Karta(char*, int);

void Druk();
};  

background image

void main()
{
clsscr();

X k1,k2(“fiolet”);

k1.Druk();
k2.Druk();
strcpy(k1.kol,”biala”);
strcpy(k2.kol, k1.kol);
k1.wym+=10;
k2.wym=k1.wym*5;
cout<<endl;

k1.Druk();
k2.Druk();
cout<<”\n\n”<,”size=”<<si
zeof(k1);
cout<<”\n\n”<,”size=”<<si
zeof(X);
 
getch();
}

void main()
{
clsscr();

Karta 
k1(“czarna”,5),k2(“czerwona”,8);

k1.Druk();
k2.Druk();
strcpy(k1.kol,”czarno-
czerwona”);
k2.wym=28;
cout<<endl;

k1.Druk();
k2.Druk();
getch();
}

background image

X::X(char* s,int w)
{
wym=w;
strcpy(kol,s);
}
void X::Druk()
{
cout<<”\nkolor=”<<kol<<”,w
ymiar=”<<wym;
}

Karta::Karta(char* s,int w): 
wym(w)
{
strcpy(kol,s);
}
void Karta::Druk()
{
cout<<”\nkolor=”<<kol<<”,wym
iar=”<<wym;
}

background image

Cechy obiektów

• Poprzez sposób definiowania obiektu decydujemy o 

zakresie ważności jego nazwy czyli także o czasie jego 

życia.

• Jeśli obiekt jest definiowany w dostępie publicznym to 

rozumiemy, że jest dostępny globalnie (uwaga na Grębosza 

i pomyłkę pomiędzy pojęciem zmiennej typu wbudowanego 

i obiektem) czyli mogą z niego korzystać wszystkie funkcje i 

obiekty innych klas w programie.

• Obiekt może funkcjonować lokalnie (obiekt prywatny) i 

wówczas automatycznie kończy się jego zakres ważności 

wtedy, kiedy fragment programu (klasa, blok) pozostaje 

zakończona faktycznie. Taki obiekt –podobnie jak zmienna 

lokalna – traci swoje cechy (pomimo hermetyzacji) w 

zakresie wartości jego zmiennych i metod. Taki obiekt, 

podobnie jak zmienną lokalną, będziemy uważać za 

zapisywany automatycznie.

background image

Cechy obiektów

• Obiekt globalny jest inicjalizowany inaczej niż lokalny bo 

wstępnie (zanim zacznie funkcjonować jako konkret) jest 
inicjowany zerami.

• Obiekt mogę powołać do życia jako obdarzony atrybutami. 

W szczególności może to być atrybut static. Taki obiekt, 
nawet jeśli jest lokalny, zachowa swoje wartości zmiennych 
i metod takie, jak przy ostatnim komunikacie. Inicjalizacja 
jest tu podobna jak obiektu globalnego – wartościami 
zerowymi.

• Jeśli atrybutu static użyjemy do nazwy globalnej, to może 

ona być dostępna TYLKO W SWOIM PLIKU. Oznacza to, że 
nie mogę uzyskać dostępu do takiego obiektu wtedy, kiedy 
jest on w pliku dołączonym dyrektywą preprocesora include 
jako plik nagłówkowy.

background image

this - WSKAŹNIK SPECJALNY 

• Każdej funkcji - metodzie zadeklarowanej wewnątrz klasy 

zostaje 

w momencie wywołania w niejawny sposób (ang. implicitly) 

przekazany wskaźnik do obiektu (w stosunku do którego 

funkcja ma

zadziałać). Pointer wskazuje funkcji w pamięci ten obiekt, 

którego członkiem jest dana funkcja. Bez istnienia takiego 

właśnie wskaźnika nie moglibyśmy stosować spokojnie 

funkcji, nie 

moglibyśmy odwoływać się do pola obiektu, gdybyśmy nie 

wiedzieli 

jednoznacznie, o który obiekt chodzi. 

• Wskaźnik this jest pierwszym argumentem konstruktora 

obiektu. W konstruktorze wskazuje on na nie 

zainicjalizowany fragment pamięci, a rolą konstruktora jest 

właśnie inicjalizacja na rzecz konkretnego obiektu 

(konkretu).

background image

this - WSKAŹNIK SPECJALNY, c.d.

• Program posługuje się automatycznie niejawnym 

wskaźnikiem do obiektu (ang. implicit pointer). Możemy 

wykorzystać ten istniejący, choć do tej pory nie widoczny 

dla nas pointer posługując się słowem kluczowym this (ten). 

This pointer wskazuje na obiekt, do którego należy 

funkcja. Korzystając z tego wskaźnika funkcja może bez 

cienia 

wątpliwości zidentyfikować właśnie ten obiekt, z którym 

pracuje 

a nie obiekt przypadkowy. 

[!!!] FUNKCJE KATEGORII static NIE OTRZYMUJĄ POINTERA 

this. 

Należy pamiętać, że wskaźnik this istnieje wyłącznie 

podczas 

wykonywania metod (ang. class member function 

execution), za 

wyjątkiem funkcji statycznych. 

background image

Tworzenie obiektów

Kiedy w C++ tworzony jest obiekt zachodzą dwa procesy:

1.

przydział pamięci do obiektu

2.

Wywołanie konstruktora inicjalizującego tę pamięć. 

Pierwszy proces może być wykonany na różne sposoby i w różnym 

czasie:

a.

Pamięć może zostać przydzielona, zanim zacznie się praca 

programu –w obrębie obszaru danych statycznych. Obszar ten 

istnieje przez cały czas działania programu.

b.

Pamięć może zostać przydzielona na stosie kiedy zostanie 

osiągnięty określony punkt realizacji programu (klamrowy 

nawias otwierający). Jest ona zwalniana po pojawieniu się 

klamrowego nawiasu zamykającego. Tutaj potrzebna jest wiedza 

o liczbie i rozmiarze wykorzystywanych zmiennych aby nie 

przekroczyć rozmiaru stosu.

c.

Pamięć jest przydzielana na stercie. Jest to proces dynamiczny. 

Jest on obsługiwany odpowiednia funkcją. Czyli przydziela ją i 

zwalnia program (czytaj programista) [malloc(), free() w 

<cstdlib>, new, delete w standardowej bibliotece poprzez 

przestrzeń nazw]


Document Outline