Programowanie obiektowe, w2, 2


2. Klasy

2.1. Funkcje składowe i enkapsulacja.

W C++ kiedy deklarujemy zmienne typu struktury - nazwiemy je w dalszym ciągu obiektami - deklarujemy na ogół funkcje do manipulowania tymi obiektami. W złożonych programach mamy obiekty wielu typów i kilka funkcji dla każdego typu obiektów. W C++, obsługujące obiekty zdefiniowane przez strukturę danego typu możemy wcielić do tej struktury. Ponieważ funkcje te są zadeklarowane (lub nawet zdefiniowane) wewnątrz klasy, chwyt ten nazywamy enkapsulacją (kapsułkowaniem) - funkcje i dane zostały zamknięte w "kapsułce".

Poniżej mamy przykład takiego programu. W klasie punkt zawarto dwie funkcje

drukpunkt() i ustawpunkt().

// program PUN1

#include <iostream.h>

main()

{

struct punkt

{

float xx, yy;

void ustawpunkt(float x, float y){xx=x; yy=y;}

void drukpunkt() {cout << xx << ' ' << yy << endl;}

};

punkt u,v;

u.ustawpunkt(1.0, 2.0);

u.drukpunkt();

v.ustawpunkt(3.0, 4.0);

v.drukpunkt();

}

Zwróćmy uwagę na cztery wywołania funkcji w programie. Wywołanie na przykład

u.ustawpunkt(1.0, 2.0)

ma tę samą formę, co odwołanie się do składowej u.xx.

Wynikiem programu PUN1 będzie czwórka liczb:

1 2

3 4

W programie PUN1 obie funkcje ustawpunkt i drukpunkt zostały zdefiniowane wewnątrz struktury. Kompilator reaguje wówczas tak samo, jakbyśmy przed funkcjami umieścili słowo kluczowe inline. Jeśli nie chcemy takiego efektu, możemy te funkcje jedynie zadeklarować wewnątrz klasy. Pokazuje to program PUN2.

// Program PUN2

#include <iostream.h>

struct punkt

{

float xx, yy;

void ustawpunkt(float, float);

void drukpunkt();

};

void punkt::ustawpunkt(float x, float y)

{

xx=x;

yy=y;

}

void punkt::drukpunkt()

{

cout << xx << ' ' << yy << endl;

}

main()

{

punkt u,v;

u.ustawpunkt(1.0, 2.0);

u.drukpunkt();

v.ustawpunkt(3.0, 4.0);

v.drukpunkt();

}

Funkcje ustawpunkt i drukpunkt nie są tu funkcjami wewnętrznymi. Zauważmy, że w definicjach funkcji pojawił się człon

vector::

jest on niezbędny, by było jasne, że funkcje są składowymi struktury punkt. Możemy w związku z tym stosować nazwy xx i yy nie zaznaczając, że są one składowymi struktury punkt. Możemy również - by podkreślić, że xx i yy są składowymi struktury - wykorzystać notację

this -> xx

this ->yy

Nazwa this jest słowem kluczowym w C++. W funkcji składowej - metodzie - oznacza ono wskaźnik do obiektu, z którego ta funkcja została wywołana. Na przykład, przy wywołaniu:

u.drukpunkt();

przy wykonaniu tej funkcji this jest wskaźnikiem do obiektu u, więc this->xx jest równoważny z zapisem u.xx.

Wskaźnik this jest wskaźnikiem stałym, podobnie jak nazwy tablic. Zatem jeśli nasz obiekt jest klasy X, to wskaźnik this ma typ X const *.

W programie PUN1, typ punkt jest lokalny w funkcji main, w PUN2 nie jest to możliwe, ponieważ notacja punkt:: musi być użyta poza tą funkcją.

Trzeba jeszcze zauważyć, że choć wyrażenia u.xx oraz u.drukpunkt() wyglądają podobnie istnieje między nimi istotna różnica: każdy z obiektów u i v posiada swoją składową liczbową xx, ale oba obiekty wykorzystują jedną, wspólną funkcję drukpunkt.

2.2. Dostęp do składowych

W programach PUN1 i PUN2 moglibyśmy odwoływać się do składników struktury xx i yy. Nie robiliśmy tego - dostęp uzyskiwaliśmy za pośrednictwem funkcji ustawpunkt i drukpunkt. Taka sytuacja występuje bardzo często. Zwłaszcza jeśli określamy bardzo złożony typ zmiennej, korzystne jest sterowanie dostępem do składowych naszej struktury:

1. Niektóre składowe, takie jak funkcje ustawpunkt i drukpunkt w programach PUN1 i PUN2, powinny być publiczne. Należą one do tak zwanego interfejsu - "użytkownik" programu musi wiedzieć jedynie, jak się nimi posługiwać.

2. Inne składowe, takie jak xx oraz yy, powinny być prywatne. Należą one do tak zwanej implementacji - użytkownicy nie muszą znać szczegółów, muszą tylko wiedzieć, jakie operacje mogą na nich wykonać.

Kiedy stosujemy słowo kluczowe struct, wszystkie ich składowe są domyślnie publiczne. Jeśli stosujemy słowo kluczowe class, składowe są domyślnie prywatne. Możemy dokładnie sprecyzować, czego chcemy, stosując słowa kluczowe public i private. (Jeżeli pominiemy oba te słowa pracując z klasą, nie będziemy mieli dostępu do żadnej ze składowych, co nie ma sensu). Jest to jedyna różnica między klasami i strukturami. W deklaracji klasy stosujemy słowa public i private z dwukropkami:

//Program PUN3

#include <iostream.h>

class punkt

{

private:

float xx, yy;

public:

void ustawpunkt(float, float);

void drukpunkt();

};

void punkt::ustawpunkt(float x, float y)

{

xx=x;

yy=y;

}

void punkt::drukpunkt()

{

cout << xx << ' ' << yy << endl;

}

main()

{

punkt u,v;

u.ustawpunkt(1.0, 2.0);

u.drukpunkt();

v.ustawpunkt(3.0, 4.0);

v.drukpunkt();

}

W powyższym programie moglibyśmy pominąć słowo "private:", gdyż ta cecha składników klasy jest domyślna.

Moglibyśmy pewną klasę zadeklarować tak:

class dzika

{

int a;

float b;

void fun1(int);

protected:

char m;

void fun2(void);

public:

int x;

void fun3(char);

private:

int d;

public:

void fun4(void);

};

W powyższym przykładzie następujące składniki klasy są prywatne: a, b, fun1, d, zaś składniki publiczne to x, fun3, fun4. występuje tutaj jeszcze jedno słowo kluczowe: protected w odniesieniu do składników m i fun2. Słowa tego używamy przy dziedziczeniu klas - składowe opatrzone tym słowem są zastrzeżone dla samej klasy i klas potomnych.

Powyższy styl programowania nie jest najlepszy - lepiej jest składowe o określonym dostępie zgrupować razem.

Uwaga.

W definicjach składników funkcyjnych klasy nazwa klasy z czterema kropkami to integralna część nazwy funkcji. Zatem gdy funkcja zwraca wskaźnik , trzeba pisać:

char *klasakot::mojafunkcja()

{.........}

Gwiazdka powinna być przed pełną nazwą, a nie przed nazwą wewnętrzną funkcji w klasie.

Wewnątrz klasy (tzn. wewnątrz jej funkcji składowych) pewne składowe klasy (również funkcje) możemy zasłonić. Trzeba się wówczas do nich odwoływać za pośrednictwem operatora ::. Ilustruje to następujący przykład:

// program zasl.

#include <iostream.h>

void drukpunkt(); //funkcja globalna

int xx=25.0; //nazwa globalna

class punkt

{

private:

float xx, yy;

public:

void ustawpunkt(float, float);

void drukpunkt();

void demo();

};

void punkt::ustawpunkt(float x, float y)

{

xx=x;

yy=y;

}

void punkt::drukpunkt()

{

cout << xx << ' ' << yy << endl;

}

void punkt::demo()

{

cout << "xx - skladnik klasy = " << xx << endl;

cout << "xx - zmienna globalna = " << ::xx <<endl;

// zaslanianie skladowych

char xx = 'W';

cout << "\nPo zdefiniowaniu zmiennej lokalnej\n";

cout << "xx - zmienna lokalna = " << xx << endl;

cout << "xx - skladnik klasy = " << punkt::xx << endl;

cout << "xx - zmienna globalna = " << ::xx <<endl;

//zaslanianie funkcji

drukpunkt();

int drukpunkt = 7; // zasloniecie nazwy funkcji

punkt::drukpunkt(); // tak wywolujemy

}

main()

{

punkt u,v;

u.ustawpunkt(1.0, 2.0);

u.drukpunkt();

v.ustawpunkt(3.0, 4.0);

v.drukpunkt();

v.demo();

drukpunkt();

}

void drukpunkt()

{

cout << "Ta funkcja nie ma nic wspólnego ze skladnikiem klasy" << endl;

}

Program ten da następujące wyniki:

1 2

3 4

xx - skladnik klasy = 3

xx - zmienna globalna = 25

Po zdefiniowaniu zmiennej lokalnej

xx - zmienna lokalna = W

xx - skladnik klasy = 3

xx - zmienna globalna = 25

3 4

3 4

Ta funkcja nie ma nic wspólnego ze skladnikiem klasy

2.3. Składowa statyczna.

Każdy obiekt ma swoje własne dane, natomiast funkcje zadeklarowane w klasie są wspólne dla wszystkich obiektów tej klasy. Zachodzi potrzeba, by określić zmienną wspólną dla wszystkich obiektów danej klasy. Zagadnienie to rozwiązuje składowa statyczna.

Deklarujemy taką daną wewnątrz klasy, np.:

static int numer;

Deklarując składową statyczną w ciele klasy, nie zdefiniowaliśmy jej jeszcze (nie ma dla niej zarezerwowanego miejsca w pamięci). Musimy ją zdefiniować tak jak zmienną globalną - poza wszelkimi funkcjami. Definicja tej składowej może zawierać jej inicjalizację.

int klasa::numer=6;

Inicjalizacja taka może nastąpić nawet wówczas, gdy składowa jest private. Po inicjalizacji natomiast składowa statyczna nie może być odczytywana i zapisywana przez nieuprawnione do tego funkcje.

Do składowej statycznej możemy odwołać się na trzy sposoby:

klasa::składowa

obiekt.składowa jeśli istnieją już obiekty danej klasy

wsk->składowa gdzie wsk jest wskaźnikiem do dowolnego obiektu danej klasy.

2.4. Statyczne funkcje składowe.

Funkcje składowe danej klasy również mogą być statyczne. Służą one przede wszystkim do obsługi statycznych składowych danej klasy. Podobnie jak to ma miejsce w przypadku składowych statycznych - zmiennych, również funkcje takie możemy wywołać, nie odwołując się do konkretnych obiektów, np.:

klasa::funkcja(coś)

W funkcji tej nie ma wskaźnika this - bo funkcja jest wspólna dla wszystkich obiektów danej klasy. Nie możemy się zatem odwoływać bezpośrednio do nazw składowych niestatycznych w klasie. Nazwy tych funkcji musimy "wzbogacić" o nazwy obiektów.

Niech będzie na przykład dana klasa kot i obiekt Mruczek. Niech w klasie kot będzie funkcja statyczna mruczenie() i niestatyczna składowa uszy. Niech kot *mr będzie wskaźnikiem na obiekt Mruczek.

W ciele funkcji mruczenie() do składowej uszy musimy się odwoływać tak:

Mruczek.uszy lub

mr->uszy.

Natomiast funkcja mruczenie() "widzi" prywatne składniki wszystkich obiektów klasy kot.

2.5.Funkcje składowe typu const oraz volatile.

Deklaracja takich funkcji wygląda na przykład następująco:

void kot(void) const;

void pies(void) volatile;

W definicji funkcji te słowa kluczowe stawiamy tuż przed nawiasami otwierającymi ciało funkcji.

Funkcja stała nie może nic zmieniać w obiektach. Funkcja taka może być wywołana w obiekcie const, w odróżnieniu od zwykłych funkcji składowych.

W obiekcie volatile możemy wywołać tylko funkcje volatile. Funkcja może mieć jednocześnie atrybut const i volatile.

Wskaźniki do składowych klas.

Rozpatrzmy deklarację:

class num

{

public:

float x,y;

} u,v;

Chcemy wykorzystać zmienną wskaźnikową, by przechowywać adresy składowych x obiektów u i v, a potem przypisać tym składowym jakieś konkretne wartości. Możemy to zrobić na przykład tak:

float *p;

p = &u.x; *p = 1;

p = &v.x; *p =2;

Wartość wskaźnika do liczby typu float określaliśmy dwukrotnie. Możemy to wykonac jednokrotnie:

float num::*q; // wskaźnik do składowych klasy num typu float.

q = &num::x;

u.*q = 1;

v.*q = 2;

Za pomocą tego wskaźnika nie możemy co prawda odwołać się do zwykłej zmiennej typu float, możemy jednak odwołać się do innych składowych typu float klasy num:

q = &num::y;

u.*q = 3;

v.*q = 4;

Prócz tego możemy stosować konstrukcje w rodzaju:

num *pu, *pv;

pu = &u; pv = &v;

pu->*q = 1;

pv->*q = 2;

Do zagadnienia wskaźników do składowych klasy jeszcze powrócimy.

2.6. Konstruktory i destruktory.

2.6.1. Podstawowe własności

Konstruktor to specjalna funkcja składowa. Funkcja ta zostaje uruchomiona automatycznie w chwili tworzenia obiektu. Nie tworzy ona samego obiektu (nie przydziela pamięci na obiekt) - wykonuje jedynie czynności, które jej zleciliśmy.

Destruktor to również specjalna funkcja składowa. Jest uruchomiona automatycznie, gdy obiekt przestaje istnieć.

Własności konstruktorów.

1. Konstruktor nazywa się tak jak klasa.

2. Konstruktor nie może zwracać żadnej wartości.

3. Konstruktor nie może mieć wyspecyfikowanego typu wartości zwracanej (nawet typu void).

4. Konstruktor nie może mieć atrybutów takich jak const, volatile, static czy virtual.

5. Konstruktor może być przeładowywany (tzn. w jednej klasie może być kilka konstruktorów)

6. Nie można posłużyć się adresem konstruktora.

Własności destruktorów.

1. Destruktor nazywa się tak jak klasa, ale przed nazwą klasy dodajemy znak falkę: "~".

2. Destruktor nie zwraca żadnej wartości i nie może mieć specyfikacji nawet typu void.

3. Destruktor nie może być const, valatile, static ani virtual.

4. Destruktor nie może mieć parametrów, w związku z czym nie można go przeładować - w klasie może być co najwyżej jeden destruktor.

5. Nie można posłużyć się adresem destruktora.

Rozpatrzmy przykład - klasę z dwiema składowymi z danymi:

1. Ze wskaźnikiem wsk, wskazującym na ciąg liczb całkowitych.

2. Ze zmienną całkowitą dlug, oznaczająca długość tego ciągu. Załóżmy, że zadeklarowaliśmy klasę wiersz z powyższymi składowymi. Funkcja

void dwawiersze()

{

wiersz r,s;

....

}

stworzy dwa obiekty r i s. Załóżmy, że chcemy, by jeden obiekt zawierał ciąg o długości 3, a drugi - ciąg o długosci 5. Możemy napisać wewnątrz funkcji dwawiersze:

r.wsk = new int[3];

r.dlug = 3;

s.wsk = new int[5];

s.dlug = 5;

Kiedy funkcja skończy działać, zwolni się i automatycznie w pamięci miejsce na oba obiekty r i s, ale nie na dwa wskazywane przez nie ciągi. Dlatego w funkcji dwawiersze trzeba by włączyć dodatkowe dwie instrukcje:

delete r.wsk;

delete s.wsk;

Jest to potencjalne źródło błędów. Znacznie lepiej jest zrealizować to za pomocą konstruktora i destruktora.

// Program KONSTR. Demonstracja konstruktora i destruktora.

#include <iostream.h>

class wiersz

{

int *wsk, dlug;

public:

wiersz(int = 3);

~wiersz() {delete wsk;}

void drukwiersz(char *);

};

wiersz::wiersz(int n)

{

wsk = new int[n];

dlug = n;

for(int i=0; i<n; i++) wsk[i] = 10*i;

}

void wiersz::drukwiersz(char *nap)

{

cout << nap;

for(int i=0; i<dlug; i++)cout << wsk[i] << ' ';

cout << endl;

}

void dwawiersze()

{

wiersz r, s(5);

r.drukwiersz("r: ");

s.drukwiersz("s: ");

}

main()

{dwawiersze();}

Konstruktor i destruktor klasy wiersz jest wołany dwukrotnie. Konstruktor wiersz ma argument domyślny 3, który odnosi się do r, gdyż w deklaracji

wiersz r, s(5)

r nie ma argumentu. Nie można zastąpić r wyrażeniem r(). Konstruktor uruchamiany w takim przypadku jest tak zwanym konstruktorem domniemanym - konstruktorem, który można wywołać bez żadnych argumentów (tzn. konstruktor może mieć na przykład wszystkie argumenty domyślne).

Gdyby w powyższym programie nie umieszczono destruktora ~wiersz, wielokrotne tworzenie i usuwanie obiektów mogłoby doprowadzić do przepełnienia pamięci.

2.6.2. Obiekty tworzone dynamicznie.

W programie KONSTR konstruktor wiersz() był wywołany, kiedy deklaracja

wiersza r, s();

stworzyła obiekty.

Obiekty można również tworzyć dynamicznie. W naszym przykładzie moglibyśmy napisać:

wiersz *r;

co nie prowadziłoby do stworzenia obiektu r i wywołania konstruktora obiektu. Obiekt tworzymy instrukcją:

p = new row;

Dopiero wtedy konstruktor jest wywoływany. Podobnie, obiekt jest niszczony (co pociąga za sobą wywołanie destruktora), instrukcją:

delete p;

2.4.3. Konstruktor jako narzędzie konwersji typów.

Rozpatrzmy program

//Program PUN4

#include <iostream.h>

class punkt

{

float xx, yy;

public:

punkt(float = 0, float =0);

void drukpunkt();

};

punkt::punkt(float x, float y)

{

xx=x;

yy=y;

}

void punkt::drukpunkt()

{

cout << xx << ' ' << yy << endl;

}

main()

{

punkt u(1.0,2.0),v(5);

u.drukpunkt();

v.drukpunkt();

}

Konstruktory są funkcjami, można więc rozpatrywać wyrażenie

punkt(5)

jako wywołanie funkcji. Z drugiej strony, jest to także typowa notacja C++ operatora rzutu, podobna na przykład do

float(5).

Napis punkt(5) tworzy obiekt ze składowymi xx=5 i yy-0 i obiekt ten jest rezultatem rzutowania. Jednym ze sposobów określania konwersji typów dla klas jest po prostu definiowanie właściwych konstruktorów. Porównajmy następujące dwa wiersze:

float x; x = float(5);

punkt v; v = punkt(5);

Możemy uprościć ten fragment programu, inicjalizując zmienne u i v w ich deklaracji:

float x = float(5);

punkt v = punkt(5);

lub jeszcze prościej:

float x = 5;

punkt v = 5;

Ta ostatnia deklaracja v jest równoważna wyrażeniu

punkt v(5);

użytemu w PUN4. Podobnego konstruktora dla typu float nie ma, nie można więc napisać

float x(5) // NIEPOPRAWNE.

Oto jeszcze jeden kawałek programu:

x =5;

v = 5;

Przypisanie v = 5 jest skróconą formą wyrażenia v = punkt(5). Widzimy stąd, że konstruktory przydają się również w konwersjach niejawnych. Konwersje takie występują nie tylko w instrukcjach przypisania. Mogą się zdarzyć na przykład

a) Przy wywołaniu funkcji, której argumentem jest obiekt punkt -f(1).

b) Przy instrukcji return 3; w funkcji, która zwraca punkt.

c) Przy obliczaniu wyrażeń w których występują przeładowane operatory arytmetyczne.

Uwaga!

punkt v = 5; jest równoważny punkt v(5);

punkt v=punkt(7,3); jest równoważny punkt v(7,3);

punkt v=(7,3); jest równoważny punkt(3);

2.6.4. Lista inicjalizacyjna konstruktora.

Składową klasy może być obiekt stały, np.

const int N;

Stała N nie może być zainicjalizowana w ciele klasy - deklaracja klasy nie tworzy obiektów. Nie można zatem napisać w ciele klasy: const int N = 45;

Do nadawania wartości takim zmiennym służy lista inicjalizacyjna konstruktora. Oto przykład tej konstrukcji.

class kot

{

const int N;

float x;

char c;

kot(float, int, char); // konstruktor - deklaracja

}

kot::kot(float xx, int NN, char cc): N(NN), c(cc)

{

x=xx;

} //koniec definicji konstruktora

Lista inicjalizacyjna to to, co znajduje się w definicji konstruktora między dwukropkiem po nagłówku a nawiasem { otwierającym ciało konstruktora. Lista składa się z kilku elementów oddzielonych przecinkami. Zapis N(NN) należy rozumieć: zainicjalizuj składową N wartością NN.

Jak widać z powyższego przykładu, w liście inicjalizacyjnej możemy umieścić nie tylko składowe stałe danej klasy. W szczególności możemy umieścić tam wszystkie składowe, które dadzą się zainicjować. Jednak składowych stałych nie można zainicjować inaczej - tylko za pomocą listy inicjalizacyjnej.

Listę inicjalizacyjną stosujemy również wówczas, gdy komponentem klasy jest obiekt innej klasy - w liście inicjalizacyjnej umieszczamy wywołanie konstruktora tego obiektu. Również przy dziedziczeniu mamy do czynienia z tą listą. Wrócimy jeszcze do tych tematów.

2.6.5. Konstruktor kopiujący.

Kopiowanie obiektu odbywa się w następujących sytuacjach:

1. W programie, w instrukcji przypisania x=y.

2. Przy deklaracji obiektów np. dla klasy kot:

kot pers, angora=pers;

3. Przy wywoływaniu funkcji, której parametrami są obiekty danej klasy przekazywane przez wartość.

4. Przy zwracaniu przez funkcję obiektów danej klasy przez wartość (tu powstaje chwilowa kopia obiektu wewnętrznego).

W sytuacji 1 kopiowanie odbywa się za pomocą operatora przypisania. W sytuacjach 2-4 za pomocą konstruktora kopiującego.

Jeśli nie zrobimy nic, konstruktor posłuży się standardowym operatorem przypisania i standardowym konstruktorem kopiującym. Urządzenia te przepiszą nasze obiekty "komponent po komponencie". Jest to tak zwana "płytka kopia". Jeśli pewne komponenty klasy są wskaźnikami, z przydzielonymi obszarami pamięci, wskaźniki w takiej kopii będą wskazywały na ten sam obszar pamięci wskaźniki w oryginale. Doprowadzi to do kłopotów przy kasowaniu obiektów - może nastąpić próba wielokrotnego kasowania tego samego obszaru pamięci, co kończy się na ogół zawieszeniem programu.

Dlatego w takich przypadkach lepiej przeładować operator przypisania i napisać własny konstruktor kopiujący.

Konstruktor kopiujący ma formę

klasa::klasa(klasa &);

Oto przykład wykorzystania konstruktora kopiującego

// Program KONSTR1. Demonstracja konstruktora kopiujacego.

#include <iostream.h>

class wiersz

{

int *wsk, dlug;

public:

wiersz(int = 3);

wiersz (wiersz &); // konstruktor kopiujacy

~wiersz() {delete wsk;}

void drukwiersz(char *);

};

wiersz::wiersz(int n)

{

wsk = new int[n];

dlug = n;

for(int i=0; i<n; i++) wsk[i] = 10*i;

}

wiersz::wiersz(wiersz &oryg)

{

dlug=oryg.dlug;

wsk=new int[dlug];

for (int i=0; i < dlug; i++)

wsk[i]=oryg.wsk[i];

}

void wiersz::drukwiersz(char *nap)

{

cout << nap;

for(int i=0; i<dlug; i++)cout << wsk[i] << ' ';

cout << endl;

}

void trzywiersze()

{

wiersz r, s(5),z=r;

r.drukwiersz("r: ");

s.drukwiersz("s: ");

z.drukwiersz("z: ");

}

main()

{trzywiersze();}

Konstruktor kopiujący skopiował zarówno składowe oryginalnego obiektu, jak i wykorzystany bufor.

21

Piotr Staniewski

C++ Studia Zaoczne. Wykład 2.



Wyszukiwarka

Podobne podstrony:
Programowanie obiektowe(ćw) 1
Zadanie projekt przychodnia lekarska, Programowanie obiektowe
Programowanie obiektowe w PHP4 i PHP5 11 2005
Programowanie Obiektowe ZadTest Nieznany
Egzamin Programowanie Obiektowe Głowacki, Programowanie Obiektowe
Jezyk C Efektywne programowanie obiektowe cpefpo
Programowanie Obiektowe Ćwiczenia 5
Programowanie obiektowe(cw) 2 i Nieznany
programowanie obiektowe 05, c c++, c#
Intuicyjne podstawy programowania obiektowego0
Programowanie obiektowe, CPP program, 1
wyklad5.cpp, JAVA jest językiem programowania obiektowego
projekt01, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
przeciazanie metod i operatorow, Programowanie obiektowe
projekt06, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
projekt07, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
Programowanie Obiektowe Cz2, Dziedziczenie proste
Programowanie obiektowe, wyklad6-czesc1, Dziedziczenie wielobazowe

więcej podobnych podstron