Klasy obiektów
Wykład 2
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.
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ład: program 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.
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.};
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();
}
Karta::Karta(char* s,int w): wym(w)
{
strcpy(kol,s);
}
Definicja
konstrukto
ra
Inicjalizacja
argumentu
konstruktora
Definicja
funkcji
void Karta::Druk()
{
cout<<”\nkolor=”<<kol<<”,wymiar=
”<<wym;
}
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()
•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.
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();
};
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();}
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);
}
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();
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
.
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.
•
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. 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
.
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();
};
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();
}
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<<”,wy
miar=”<<wym;
}
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.
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.