Obiekty i metody stałe

Transkrypt

Obiekty i metody stałe
Obiekty i metody stałe
Bogdan Kreczmer
ZPCiR IIAiR PWr
pokój 307 budynek C3
[email protected]
c 2005–2008 Bogdan Kreczmer⋆
Copyright ⋆ Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepiony
˛
pod warun-
kiem wykorzystania wyłacznie
˛
do własnych prywatnych potrzeb i może on być kopiowany wyłacznie
˛
w całości, razem z niniejsza˛
strona˛ tytułowa.
˛
Definiowanie stałych i ich znaczenie
const int StalaInt = 5;
int
ZmienInt = 4;
char Tablica1[StalaInt];
hh
hhh
hhhh
char Tablica2[ZmienInt];
hhh
int main()
{
hhhh
hhhh
StalaInt
= hZmienInt;
hhh
// Dopuszczalna w ISO/ANSI C++ ale nie w ANSI C
// Niedopuszczalne w ISO/ANSI C++ i ANSI C
// To podstawienie jest niedopuszczalne.
ZmienInt = StalaInt;
return 0;
}
Obiekty stałe i metody stałe
1
Wskaźniki i modyfikator const
char const ∗ wsk;
(const char ∗)
char ∗ const wsk;
1111111
0000000
0000000
1111111
0000000
1111111
0000000
1111111
0000000
1111111
1111111111111
0000000000000
0000000000000
1111111111111
0000000000000
1111111111111
0000000000000
1111111111111
0000000000000
1111111111111
0000000000000
1111111111111
w
1111111111111111111111
0000000000000000000000
0000000000000000000000
1111111111111111111111
0000000000000000000000
1111111111111111111111
0000000000000000000000
1111111111111111111111
0000000000000000000000
1111111111111111111111
l ó d k a
0000000000000000000000
1111111111111111111111
0000000000000000000000
1111111111111111111111
w
ó
d
k
a
l
Obiekty stałe i metody stałe
2
Definiowanie stałych i ich znaczenie
const char ∗NapisStaly
char ∗ const WskStaly
char ∗ WskNapisu
= "Jakiś łańcuch";
= “Taka inicjalizacja jest ryzykowna”;
= “Równie zła inicjalizacja jak powyżej”;
int main()
{
hhh
hhhh
hh"Inny
WskStaly
=
hhh łańcuch";
WskStaly[1] = ’!’;
hh
hhh
hhhh
NapisStaly[3]
= ’$’;
hhh
NapisStaly = "Inny łańcuch";
NapisStaly = WskStaly;
hhhh
hhhh
WskStaly
= hNapisStaly;
hhh
// To podstawienie jest niedopuszczalne.
// Tutaj powinna nastapić
˛ katastrofa.
// Ta operacja jest niedopuszczalna
// Ta operacja jest niedopuszczalna.
}
Obiekty stałe i metody stałe
3
Definiowanie parametrów stałych
char ∗Kopiuj( char ∗ Cel, const char ∗Zrodlo ) /*
{
hhhh
hhhh
Zrodlo[2]
/* Tego na pewno być nie może
h=
hh’x’;
h
...
Zrodlo = Zrodlo + 1;
/* Takie operacje dopuszczamy
hhhh
hhhh
hhh
h
*/
*/
*/
return Zrodlo;
/* To jest również niemożliwe
*/
return Cel;
} /*
/* Ten wskaźnik możemy przekazać
*/
*/
int main()
{
char Tablica[30];
Kopiuj( Tablica, ”łódka” );
...
Kopiuj( Tablica, ”łódka” )[0] = ’w’;
...
Obiekty stałe i metody stałe
4
Obiekty stałe
class LiczbaZespolona {
public:
float re, im;
};
int main()
{
LiczbaZespolona
LiczbaZespolona const
float re = StalaZesp. re;
float im = StalaZesp. im;
hh
hhhh
hhhre
= re;
StalaZesp.
hhh
hhh
hhh
hhhim
= im;
StalaZesp.
hhh
. . .
ZmienZesp;
StalaZesp = ZmienZesp;
// Można zainicjalizować
// Możemy odczytać wartości
// obiektu stałego
// Ta operacja jest niedozwolona
// Ta również
}
Obiekty stałe i metody stałe
5
Obiekty stałe
class LiczbaZespolona { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
float re, im;
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main()
{
LiczbaZespolona const
StalaZesp;
hh
}
hhhh
hhhre
20;
// Ta operacja jest niedozwolona
StalaZesp.
hh=
h
((LiczbaZespolona&)StalaZesp). im = 20;
// Ta już jest poprawna
const cast<LiczbaZespolona&>(StalaZesp). im = 20;
// A to bardziej poprawne
. . .
Zmiane˛ wartości obiektu stałego można wymusić poprzez rzutowanie.
Nie jest to jednak dobry pomysł.
Obiekty stałe i metody stałe
6
Obiekty stałe
class LiczbaZespolona { //
float re, im;
public:
float Re() const { return re; } // Deklaracja metody stałej
float Im()
{ return im; }
}; //
int main()
{
LiczbaZespolona
LiczbaZespolona const
float re = StalaZesp.Re();
hhh
hhhh
hhh
float im = StalaZesp.Im();
hh
re = ZmienZesp.Re();
im = ZmienZesp.Im();
. . .
ZmienZesp;
StalaZesp = ZmienZesp;
// Tej metody nie możemy użyć
// Tu jest wszystko dobrze
}
Obiekty stałe i metody stałe
7
Metody stałe
class LiczbaZespolona { //
float re, im;
public:
float Re() const { return re; }
float Im() const { return im; }
hh
hhhh
void Zmien(float r, float i) const { rehh=hr;
im
i; }
hh=
h
double Modul() const;
}; //
// BŁAD
˛
double LiczbaZespolona::Modul() const
{
return sqrt( re∗ re + im∗ im);
}
W metodach stałych nie można dokonywać zmian wartości pól obiektu.
Można jedynie odczytywać te wartości.
Obiekty stałe i metody stałe
8
Metody stałe
class LiczbaZespolona { //
float re, im;
public:
float Re() const { return re; }
float Im() const { return im; }
double Modul() const;
}; //
double LiczbaZespolona::Modul() const
{
return sqrt(Re()∗Re() + Im()∗Im());
}
W metodach stałych można odwoływać sie˛ do innych metod stałych.
Obiekty stałe i metody stałe
9
Metody stałe
class LiczbaZespolona { //
float re, im;
public:
float Re() const { return re; }
float Im()
{ return im; }
double Modul() const;
}; //
double LiczbaZespolona::Modul() const
{
hhh
(((
h(h
hhh
((h
Im()
∗(Im());
// BŁAD
˛
return sqrt(Re()∗Re() +((
}
W metodach stałych nie można odwoływać sie˛ do metod,
które nie sa˛ metodami stałymi.
Obiekty stałe i metody stałe
10
Metody stałe
class LiczbaZespolona { //
float re, im;
public:
float Re() const { return re; }
float Im() const { return im; }
void Zmien(float r, float i) const;
}; //
// Czy ta metoda jest faktycznie stała?
void LiczbaZespolona::Zmien(float r, float i) const
{
const cast<LiczbaZespolona&>(∗this ). re = r;
const cast<LiczbaZespolona&>(∗this ). im = i;
}
// Dzieki
˛ rzutowaniu otrzy// mujemy obiekt zmienny.
Brak zezwolenia na zmiane˛ stanu obiektu w metodach stałych zawsze można
ominać
˛ wykorzystujac
˛ rzutowanie. Jednak jest to BARDZO NIEDOBRY pomysł.
Obiekty stałe i metody stałe
11
Metody stałe
class TablicaZeWskaznikiem { //
int Tablica[ROZMIAR STOSU];
int ∗ WskElem;
public:
h hh
((
h(
h(
h(
(
h{hhTablica[0] = 4; }
const
void ZmienWTablicy()
( ((
void ZmienPrzezWsk() const { ∗ WskElem = 4; }
TablicaZeWskaznikiem() { WskElem = Tablica; }
}; //
// Ta metoda nie może być stała
int main()
{
TablicaZeWskaznikiem const StStaly;
StStaly.ZmienPrzezWsk();
...
// A
jednak nastapiła
˛
zmiana !!!
Dla obiektów stałych operacje zapisu sa˛ kontrolowane jedynie na etapie kompilacji.
Obiekty stałe i metody stałe
12
Deklaracja obiektów stałych
class KlasaPusta {
public :
};
const int
hhh ((((
h
(hhh
IntStala;
h
((((
h
const KlasaPusta
ObPustyStaly;
h
hhh
(h ((((
(h
(h
h
(h
h
(hhh
(h
(h
(
(
(
h
(
(
h
h
(
(
Zarówno zwykłe zmienne jak też obiekty, które sa˛ deklarowane jako stałe, musza˛ być w momencie deklaracji zainicjalizowane. Jest to wymóg formalny i nie ma tu znaczenia zawartość obiektu.
Konieczność wykonania tej operacji wynika z faktu, że później z założenia ich zawartość jest już
niezmienna. Choć możliwe jest obejście tego ograniczenia, to jednak ma ono charakter nieformalny.
Obiekty stałe i metody stałe
13
Deklaracja obiektów stałych
class KlasaPusta {
public :
};
int
IntModyf;
const int
KlasaPusta
IntStala = IntModyf;
ObPustyModyf;
const KlasaPusta
ObPustyStaly = ObPustyModyf;
Inicjalizacje˛ można dokonać pośrednio przepisujac
˛ zawartość innej zmiennej lub
obiektu stałego lub też modyfikowalnego.
Obiekty stałe i metody stałe
14
Deklaracja obiektów stałych
class KlasaPusta {
public :
KlasaPusta( ) { }
};
const int
IntStala = 27;
const KlasaPusta
ObPustyStaly;
Inicjalizacje˛ zmiennej zadeklarowanej jako stała można dokonać poprzez przypisanie wartości stałej w momencie jej deklaracji. W przypadku obiektu za inicjalizacje˛
odpowiedzialny jest konstruktor.
Obiekty stałe i metody stałe
15
Deklaracja pól stałych
class KlasaZPoleStalym {
public :
const int
PoleStale;
KlasaZPolemStalym( ): PoleStale(27) { }
KlasaZPolemStalym( float ): PoleStale(102) { }
KlasaZPolemStalym( int ID ): PoleStale(ID) { }
};
W przypadku deklaracji pola stałego, jego inicjalizacja musi być zrealizowana w liście inicjalizacyjnej
konstruktora. Operacja ta musi być wykonana w liście inicjalizacyjnej każdego konstruktora danej
klasy. Jednak sposób inicjalizacji nie musi być jednakowy.
Mechanizm ten może być wykorzystany do rozpoznania konstruktora, który został wykorzystany przy
tworzeniu danego obiektu.
Obiekty stałe i metody stałe
16
Pola statyczne
class KlasaZPolemStatycznym {
public :
static int PoleStatyczne;
};
int main()
{
const KlasaZPolemStatycznym Ob;
}
Ob. PoleStatyczne = 5;
Pola statyczne nie sa˛ cz˛eścia˛ obiektu. Stanowia˛ one raczej cz˛eść klasy. Z tego
powodu pomimo, że obiekt został zadeklarowany jako stały, pole to może zostać
zmodyfikowane.
Obiekty stałe i metody stałe
17
Pola statyczne
class KlasaZPolemStatycznym {
public :
static int PoleStatyczne;
hhh
(((
(((hhh
const
{ hreturn PoleStatyczne; }
static int Wez( )((
static void Zmien(int Wartosc ) { PoleStatyczne = Wartosc; }
hh
(
};
int main()
{
const KlasaZPolemStatycznym
}
Ob;
Ob.Zmien(5);
Modyfikator const przy deklaracji obiektu jako obiektu stałego nie dotyczy pól statycznych, tym
samym nie może on dotyczyć również metod statycznych. Można wiec
˛ posługujac
˛ sie˛ obiektem
stałym wywoływać metode˛ statyczna,
˛ która nie jest metoda˛ stała. Dalsza˛ konsekwencja˛ jest to, że
metody statyczne podobnie jak zwykłe funkcje nie moga˛ być metodami stałymi.
Obiekty stałe i metody stałe
18
Przeciażanie
˛
metod wzgledem
˛
modyfikatora const
class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public :
PrzykladKlasy( ) { }
void Metoda( ) const { cout << ”Metoda ob. stalego” << endl; }
void Metoda( )
{ cout << ”Metoda ob. modyfikowalnego” << endl; }
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
const PrzykladKlasy
PrzykladKlasy
ObStaly;
ObModyf;
ObStaly.Metoda( );
ObModyf.Metoda( );
}
Wynik działania programu:
Metoda ob.
Metoda ob.
stalego
modyfikowalnego
Modyfikator const “wchodzi” w skład nazwy metody. Pozwala to przeciażać
˛
metody ze wzgledu
˛
na
ten modyfikator. Przy wywoływaniu metod zawsze wywoływana jest metoda posiadajaca
˛ maksymalnie dopuszczalne uprawnienia co do danego typu obiektu.
Obiekty stałe i metody stałe
19
Przeciażanie
˛
metod wzgledem
˛
modyfikatora const
class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
char Tab[ROZMIAR TAB];
public :
PrzykladKlasy( ) { strcpy( Tab, ”łódka”); }
const char ∗Tab( ) const { return Tab; }
char ∗Tab( ) { return Tab; }
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
const PrzykladKlasy
ObStaly;
cout << ObStaly.Tab( ) << endl;
(
hh(
(
h
h
h
’b’;
∗ObStaly.Tab((
)(=h(
cout << ObStaly.Tab( ) << endl;
}
Stosowanie modyfikatora const w tego typu przeciażeniach
˛
jest wymuszone przez kompilator. Pozwala to w naturalny sposób wspierać ochrone˛ spójności danych.
Obiekty stałe i metody stałe
20
Przeciażanie
˛
metod wzgledem
˛
modyfikatora const
class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
char∗ Tab;
public :
PrzykladKlasy( ) { Tab = new char [ROZMIA TAB]; strcpy( Tab, ”łódka”); }
∼
PrzykladKlasy( ) { delete [ ] Tab; }
char ∗Tab( ) const { return Tab; }
// Czy to jest dobrze?
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
const PrzykladKlasy
ObStaly;
cout << ObStaly.Tab( ) << endl;
∗ObStaly.Tab( ) = ’b’;
cout << ObStaly.Tab( ) << endl;
}
W przypadku struktur stowarzyszonych w sposób dynamiczny o tym jakie wskaźniki i w jakich momentach moga˛ być udostepniane
˛
jako stałe decyduje programista. Kompilator wspiera pojecie
˛
stałości tylko w odniesieniu do obiektu rozumianego jako ciagły
˛ obszar pamieci.
˛
Obiekty stałe i metody stałe
21
Przeciażanie
˛
metod wzgledem
˛
modyfikatora const
class PrzykladKlasy { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
char∗ Tab;
public :
PrzykladKlasy( ) { Tab = new char [ROZMIA TAB]; strcpy( Tab, ”łódka”); }
∼
PrzykladKlasy( ) { delete [ ] Tab; }
const char ∗Tab( ) const { return Tab; }
char ∗Tab( ) { return Tab; }
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
const PrzykladKlasy
ObStaly;
cout << ObStaly.Tab( ) << endl;
(
hh(
(
h
h
h
’b’;
∗ObStaly.Tab( (
) (=h(
cout << ObStaly.Tab( ) << endl;
}
Programista może decydować, które ze struktur stowarzyszonych z danym obiektem nie powinny
ulegać poprzez zdefiniowanie odpowiednich metod stałych.
Obiekty stałe i metody stałe
22
Metody stałe
Metody stałe z założenia nie powinny modyfikować stanu obiektu
(wartości atrybutów i powiaza
˛ ń z innymi obiektami). W tym sensie poje˛
cie to ma znacznie szersze niż tylko fizyczna zmiana wartości pól obiektu.
Pojecie
˛
stałości dotyczy bardziej logicznej niż fizycznej struktury obiektu.
Jest ono jedynie wspierane (na tyle dobrze na ile to tylko jest możliwe)
przez jezyk
˛
C++.
C++ nie narzucona żelaznego ograniczenia na operacje realiJezyk
˛
zowane przez metode˛ stała.
˛ Mechanizm rzutowań pozostawia furtk˛e
umożliwiajac
˛ a˛ dokonanie wyłomu w zakazie zmian stanu obiektu.
Pozwala to na zachowanie elastyczności jezyka.
˛
Należy jednak mieć świadomość, że korzystanie z tej furtki w metodach
stałych jest zwykle przejawem złego stylu programowania.
Dla logicznej spójności programu metody stałe NIE POWINNY wymuszać zmiany stanu obiektu.
Obiekty stałe i metody stałe
23
Obiekty stałe
Stan obiektu stałego po zainicjalizowaniu nie powinien być modyfikowany. Poje˛
cie stałości obiektu jest wspierane przez jezyk
˛
C++ w ten sposób, że:
• zabrania sie˛ zmiany wartości pól obiektu (sa˛ one tylko do odczytu),
• zabrania sie˛ wywoływania innych metod niż metody stałe (typu const).
Jezyk
˛
C++ nie narzuca twardych ograniczeń chroniacych
˛
obiekty stałe.
Mechanizm rzutowań umożliwia wymuszenie zmiany stanu obiektu.
Wymuszanie zmiany stanu obiektu stałego jest zwykle przejawem
niedbałego lub wrecz
˛ złego stylu programowania.
Obiekty stałe i metody stałe
24
Pytania i ćwiczenia
Dany jest fragment kodu:
class LiczbaZespolona {
public:
float re, im;
};
int main()
{
LiczbaZespolona Zm;
(∗(Liczba ∗ const)&Zm). re = 5;
return 0;
}
1. Czy dokonane rzutowanie jest dopuszczalne?
2. Czy zwykły obiekt można rzutować na obiekt stały?
3. Czy w tym przypadku nastapi
˛ poprawne zapisanie wartości 5 do pola re?
4. Jeżeli nie, to jak należy to zapisać aby operacja była poprawna (chodzi o zapis z
rzutowaniem i modyfikatorem const)?
5. Jeżeli tak, to jak należy to zapisać aby uniemożliwić taka˛ operacje?
˛
Obiekty stałe i metody stałe
25