J˛ezyki programowania – cz˛e´s´c V
Marcin Szpyrka
Katedra Automatyki
Akademia Górniczo-Hutnicza w Krakowie
2009/10
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
1/42
Paradygmaty programowania
Programowanie proceduralne
Zadecyduj, jakie chcesz mie´c procedury; stosuj najlepsze algorytmy, jakie mo˙zesz
znale´z´c.
Programowanie proceduralne polega na tym, by pisz ˛
ac program dla danego
problemu, zamieni´c ten problem na seri˛e zada´n do wykonania, realizowanych przez
funkcje. Wykonywanie takiego programu, to okre´slona sekwencja wywoła´n ró˙znych
funkcji. Wad ˛
a tej techniki programowania jest to, ˙ze zajmuje si˛e ona głównie
funkcjami, a nie dba o to, czy poszczególne dane w programie s ˛
a ze sob ˛
a zwi ˛
azane
czy te˙z rozrzucone w programie w lu´zny sposób.
Programowanie modularne
Zadecyduj, jakie chcesz mie´c moduły; podziel program w taki sposób, aby ukry´c
dane w modułach.
Modułem nazywany jest zbiór powi ˛
azanych ze sob ˛
a procedur, ł ˛
acznie z danymi, na
których te procedury działaj ˛
a.
J˛ezyk C++ dostarcza mechanizm do grupowania zwi ˛
azanych ze sob ˛
a danych,
funkcji, itd. w odr˛ebne przestrzenie nazw.
J˛ezyk C++ wspiera poj˛ecie osobnej kompilacji, mechanizm ten mo˙zna stosowa´c do
tworzenia programu jako zbioru cz˛e´sciowo niezale˙znych fragmentów.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
2/42
Paradygmaty programowania
Programowanie obiektowe
Zadecyduj, jakie chcesz mie´c typy; dla ka˙zdego typu dostarcz pełny zbiór operacji.
W C++ mo˙zna definiowa´c typy (klasy), które b˛ed ˛
a si˛e zachowywa´c (prawie) tak
samo jak typy wbudowane. Powstaje wtedy nie tylko moduł kryj ˛
acy w sobie dane,
ale wr˛ecz nowy typ danej.
Programowanie t ˛
a technik ˛
a cechuje si˛e tym, i˙z danym zgrupowanym w (obiekt) nie
mówi si˛e ju˙z jak maj ˛
a co´s zrobi´c, ale tylko co maj ˛
a zrobi´c. Niedogodno´sci ˛
a tej
techniki jest fakt, ˙ze poszczególne typy danych s ˛
a sobie obce. Znaczy to, ˙ze je´sli
funkcja mo˙ze przyj ˛
a´c jako argument jeden typ danej, to nie mo˙ze przyj ˛
a´c innego. Dla
tego innego typu musi by´c zdefiniowana osobna funkcja, cho´cby jej ciało miało by´c
identyczne jak ju˙z istniej ˛
acej.
Programowanie obiektowo orientowane
Zadecyduj, jakie chcesz mie´c typy; dla ka˙zdego typu dostarcz pełny zbiór operacji;
korzystaj ˛
ac z mechanizmu dziedziczenia, jawnie wska˙z to, co jest wspólne.
Jest to technika obiektowa wzbogacona o dziedziczenie i polimorfizm. Sprawia to, ˙ze
istniej ˛
acy kod potrafi si˛e sam zorientowa´c w trakcie pracy programu z jakim
obiektem w danej chwili pracuje i odpowiednio na to reagowa´c. Tego typu technika
odzwierciedla zale˙zno´sci klas obiektów otaczaj ˛
acych nas w realnym ´swiecie.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
3/42
Paradygmaty programowania
Programowanie uogólnione
Zadecyduj, jakie chcesz mie´c algorytmy; parametryzuj je w taki sposób, by działały
dla ró˙znych typów i struktur danych.
Programowanie uogólnione wi ˛
a˙ze si˛e z wykorzystaniem szablonów (wzorców)
funkcji i klas. Podstawowe zało˙zenie jest takie, je´sli mo˙zna wyrazi´c algorytm
niezale˙znie od szczegółów reprezentacji i mo˙zna to zrobi´c tanio oraz bez logicznych
przeinacze´n, to nale˙zy to zrobi´c.
Szablony s ˛
a mechanizmem czasu kompilacji, nie wprowadzaj ˛
a ˙zadnego narzutu
czasu wykonania w porównaniu z kodem pisanym r˛ecznie.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
4/42
Klasy
Klasa jest kluczow ˛
a koncepcj ˛
a j˛ezyka C++, realizuj ˛
ac ˛
a abstrakcj˛e danych na bardzo
wysokim poziomie. Odpowiednio zdefiniowane klasy udost˛epniaj ˛
a u˙zytkownikowi
wszystkie istotne mechanizmy programowania obiektowego: enkapsulacj˛e,
dziedziczenie, polimorfizm, a tak˙ze szablony klas i funkcji.
Elementami składowymi klasy mog ˛
a by´c struktury danych ró˙znych typów, zarówno
podstawowych, jak i zdefiniowanych przez u˙zytkownika, a tak˙ze funkcje dla
operowania na tych strukturach. Dost˛ep do elementów klasy okre´sla zbiór reguł
dost˛epu.
Klasa jest typem definiowanym przez u˙zytkownika. Deklaracja klasy składa si˛e z:
nagłówka, po którym nast˛epuje ciało klasy, uj˛ete w par˛e nawiasów klamrowych; po
zamykaj ˛
acym nawiasie klamrowym musi wyst ˛
api´c ´srednik. W nagłówku klasy
umieszcza si˛e słowo kluczowe class, a po nim nazw˛e klasy, która od tej chwili staje
si˛e nazw ˛
a nowego typu.
1
class
Point
{
2
public
:
3
int
x
;
4
int
y
;
5
char
name
;
6
7
double
distance
();
8
};
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
5/42
Klasy – przykład
1
#include
<iostream>
2
#include
<cmath>
3
using namespace
std
;
4
5
class
Point
{
// classpoint.cpp
6
public
:
7
int
x
;
8
int
y
;
9
char
name
;
10
11
double
distance
();
12
};
13
14
double
Point
::
distance
()
15
{
16
return
sqrt
(
x
*
x
+
y
*
y
);
17
}
18
19
int
main
()
20
{
21
Point p
= {10, 25,
’A’
};
22
cout
<<
p
.
name
<<
"("
<<
p
.
x
<<
","
<<
p
.
y
<<
")\n"
;
23
return
0;
24
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
6/42
Reprezentacja klas – diagramy klas UML
Klasa jest reprezentowana przez prostok ˛
at z wydzielonymi przedziałami: nazw ˛
a,
atrybutami i operacjami. W celu zwi˛ekszenia czytelno´sci, dowolny z nich mo˙zna
ukry´c b ˛
ad´z doda´c nowy (np. przechowuj ˛
acy zdarzenia lub wyj ˛
atki). Tradycyjnie
nazwa klasy zaczyna si˛e z du˙zej litery, jest pogrubiona, a w przypadku klasy
abstrakcyjnej tak˙ze pochyła.
Ka˙zdy atrybut pokazywany jest przynajmniej jako nazwa, opcjonalnie tak˙ze z typem,
widoczno´sci ˛
a, warto´sci ˛
a domy´sln ˛
a i dodatkowymi ograniczeniami. Do oznaczania
poziomów widoczno´sci u˙zywane b˛ed ˛
a symbole: + (publiczny), # (chroniony), −
(prywatny).
Ka˙zda metoda jest pokazywana przynajmniej jako nazwa, a dodatkowo tak˙ze ze
swoimi parametrami i zwracanym typem. Równie˙z w przypadku metod oznaczane s ˛
a
poziomy widoczno´sci.
+ x : int
+ y : int
+ name : char
+ distance() : double
Point
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
7/42
Deklaracja klasy
Klasa mo˙ze by´c deklarowana:
– Na zewn ˛
atrz wszystkich funkcji programu. Zakres widzialno´sci takiej klasy
rozci ˛
aga si˛e na wszystkie pliki programu.
– Wewn ˛
atrz definicji funkcji. Klas˛e tak ˛
a nazywa si˛e lokaln ˛
a, poniewa˙z jej zakres
widzialno´sci nie wykracza poza zasi˛eg funkcji.
– Wewn ˛
atrz innej klasy, jako klas˛e zagnie˙zd˙zon ˛
a (jej zakres widzialno´sci nie
wykracza poza zasi˛eg klasy zewn˛etrznej).
Przy deklarowaniu składowych obowi ˛
azuj ˛
a nast˛epuj ˛
ace ograniczenia:
– deklarowana składowa nie mo˙ze by´c inicjalizowana w deklaracji klasy;
– nazwy składowych nie mog ˛
a si˛e powtarza´c;
– deklaracje składowych nie mog ˛
a zawiera´c słów kluczowych auto, extern i
register, natomiast mog ˛
a by´c poprzedzone słowem kluczowym static.
Deklaracje elementów składowych klasy mo˙zna poprzedzi´c etykiet ˛
a public:,
protected: lub private:. Domy´slnie przyjmowana jest etykieta private.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
8/42
Regulacja dost˛epu do składowych klasy
– Wyst ˛
apienie etykiety private: oznacza, ˙ze
składowe wyst˛epuj ˛
ace po niej s ˛
a
dost˛epna jedynie dla funkcji składowych
klasy i tzw. funkcji zaprzyja´znionych
klasy, w której s ˛
a zadeklarowane.
– Wyst ˛
apienie etykiety public: oznacza, ˙ze
składowe wyst˛epuj ˛
ace po niej s ˛
a
dost˛epne dla dowolnej funkcji, a wi˛ec
równie˙z takiej, które nie jest zwi ˛
azana z
deklaracj ˛
a danej klasy.
– Wyst ˛
apienie etykiety protected: oznacza,
˙ze składowe wyst˛epuj ˛
ace po niej s ˛
a
dost˛epne jedynie dla funkcji składowych
klasy i tzw. funkcji zaprzyja´znionych
klasy, w której s ˛
a zadeklarowane, a tak˙ze
dla funkcji składowych klas pochodnych.
+ distance() : double
− x : int
− y : int
− name : char
+ getX() : int
+ getY() : int
+ getName() : char
+ setX(i : int) : void
+ setY(i : int) : void
+ setName(c : char) : void
Point
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
9/42
Regulacja dost˛epu do składowych klasy – przykład
1
class
Point
{
2
private
:
3
int
x
;
4
int
y
;
5
char
name
;
6
7
public
:
8
int
getX
();
9
int
getY
();
10
char
getName
();
11
void
setX
(int
i
);
12
void
setY
(int
i
);
13
void
setName
(char
c
);
14
double
distance
();
15
};
+ distance() : double
− x : int
− y : int
− name : char
+ getX() : int
+ getY() : int
+ getName() : char
+ setX(i : int) : void
+ setY(i : int) : void
+ setName(c : char) : void
Point
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
10/42
Składowe klasy
z Zakres widzialno´sci składowych obejmuje cały blok deklaracji klasy, bez
wzgl˛edu na to, w którym miejscu bloku znajduje si˛e ich punkt deklaracji.
z Je˙zeli klasa zawiera funkcje składowe, to ich deklaracje musz ˛
a wyst ˛
api´c w
deklaracji klasy. Funkcje składowe mog ˛
a by´c jawnie deklarowane ze słowami
kluczowymi: inline, static i virtual; nie mog ˛
a by´c deklarowane ze słowem
kluczowym extern.
z W deklaracji klasy mo˙zna równie˙z umieszcza´c definicje krótkich (1-2
instrukcje) funkcji składowych; s ˛
a one wówczas traktowane przez kompilator
jako funkcje rozwijalne, tj. tak, jakby były poprzedzone słowem kluczowym
inline.
z Funkcje składowe klasy mog ˛
a operowa´c na wszystkich zmiennych składowych,
tak˙ze prywatnych i chronionych. Mog ˛
a one równie˙z operowa´c na zmiennych
zewn˛etrznych w stosunku do definicji klasy.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
11/42
Definicja klasy
z W programach jednoplikowych dłu˙zsze funkcje składowe deklaruje si˛e w
nawiasach klamrowych zawieraj ˛
acych deklaracj˛e klasy, za´s definiuje si˛e je
bezpo´srednio po zamykaj ˛
acym nawiasie klamrowym.
z Deklaracja klasy wraz z definicjami funkcji składowych stanowi definicj˛e klasy.
z Najcz˛e´sciej definicj˛e klasy umieszcza si˛e w dwóch plikach. W pliku
nagłówkowym (z rozszerzeniem .h) umieszcza si˛e deklaracj˛e klasy, a w pliku
´zródłowym (z rozszerzeniem .cpp) umieszcza si˛e definicj˛e funkcji składowych
klasy. Plik nagłówkowy nale˙zy doł ˛
aczy´c do pliku ´zródłowego korzystaj ˛
ac z
dyrektywy #include.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
12/42
Definicja klasy – przykład (plik point.h)
1
#ifndef
POINT_H
2
#define
POINT_H
3
4
class
Point
{
5
private
:
6
int
x
;
7
int
y
;
8
char
name
;
9
10
public
:
11
int
getX
() { return
x
; }
12
int
getY
() { return
y
; }
13
char
getName
() { return
name
; }
14
void
setX
(int
i
) {
x
=
i
; }
15
void
setY
(int
i
) {
y
=
i
; }
16
void
setName
(char
c
) {
name
=
c
; }
17
double
distance
();
18
};
19
20
#endif
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
13/42
Definicja klasy – przykład (pliki point.cpp i classpoint2.cpp)
1
#include
"point.h"
2
#include
<cmath>
3
4
double
Point
::
distance
()
5
{
6
return
sqrt
(
x
*
x
+
y
*
y
);
7
}
8
9
------------------------------------------------
10
11
#include
<iostream>
12
#include
"point.h"
13
using namespace
std
;
14
15
int
main
()
16
{
17
Point p
;
18
p
.
setX
(20);
19
p
.
setY
(50);
// p.y = 50;
nielegalne
20
p
.
setName
(
’P’
);
21
cout
<<
p
.
getName
() <<
"("
<<
p
.
getX
()
22
<<
","
<<
p
.
getY
() <<
")\n"
;
23
return
0;
24
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
14/42
Wska´znik this
Funkcje składowe s ˛
a zwi ˛
azane z definicj ˛
a klasy, a nie z deklaracjami obiektów tej
klasy. Poci ˛
aga to za sob ˛
a nast˛epuj ˛
ace konsekwencje:
– Istnieje tylko jeden „egzemplarz” kodu definicji danej funkcji składowej,
b˛ed ˛
acej „własno´sci ˛
a” klasy.
– Kod ten nie wchodzi w skład ˙zadnego obiektu.
– W ka˙zdym wywołaniu funkcji składowej klasy musi by´c wskazany obiekt
wołaj ˛
acy.
Wskazanie obiektu wołaj ˛
acego jest realizowane przez przekazanie do funkcji
składowej ukrytego argumentu aktualnego, którym jest wska´znik do tego obiektu.
Wska´znikiem tym jest inicjalizowany niejawny argument – wska´znik – w definicji
funkcji składowej. Do wska´znika tego mo˙zna si˛e równie˙z odwoływa´c jawnie,
u˙zywaj ˛
ac słowa kluczowego this.
1
class
Niejawna
{
2
private
:
3
int
m
;
4
public
:
5
int
funkcja
()
6
{ return
m
; }
7
};
1
class
Jawna
{
2
private
:
3
int
m
;
4
public
:
5
int
funkcja
()
6
{ return this->
m
; }
7
};
W praktyce jawnych odwoła´n do wska´znika this nie opłaca si˛e u˙zywa´c (poza
kilkoma sytuacjami wyj ˛
atkowymi).
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
15/42
Konstruktory i destruktory
Konstruktory i destruktory nale˙z ˛
a do grupy specjalnych funkcji składowych. Grupa ta
obejmuje: konstruktory i destruktor, konstruktor kopiuj ˛
acy oraz operator przypisania.
Konstruktor jest funkcj ˛
a składow ˛
a o takiej samej nazwie, jak nazwa klasy. Nazw ˛
a
destruktora jest nazwa klasy, poprzedzona znakiem tyldy (
∼
). Ka˙zda klasa zawiera
konstruktor i destruktor, nawet gdy nie s ˛
a one jawnie zadeklarowane. Je˙zeli w klasie
nie zadeklarowano konstruktora i destruktora, to zostan ˛
a one wygenerowane przez
kompilator i automatycznie wywoływane podczas tworzenia i destrukcji obiektu.
Konstruktor jest specjaln ˛
a funkcj ˛
a składow ˛
a. W trakcie definiowania obiektu,
przydziela mu si˛e miejsce w pami˛eci, a nast˛epnie (automatycznie) uruchamiany jest
jego konstruktor. Konstruktor sam nie przydziela pami˛eci na obiekt, mo˙ze j ˛
a jedynie
zainicjalizowa´c.
Destruktor jest wywoływany automatycznie zawsze, gdy obiekt jest likwidowany.
Klasa nie musi mie´c obowi ˛
azkowo destruktora. Destruktor nie likwiduje obiekt ani
nie zwalnia obszaru pami˛eci, który obiekt zajmował. Destruktor przydaje si˛e wtedy,
gdy przed zniszczeniem obiektu trzeba jeszcze dokona´c jakich´s działa´n.
Destruktor jest potrzebny, gdy konstruktor danej klasy dokonał na swój u˙zytek
rezerwacji dodatkowej pami˛eci (operatorem new). Wtedy w destruktorze umieszcza
si˛e instrukcj˛e delete zwalniaj ˛
ac ˛
a pami˛e´c.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
16/42
Konstruktory – przykład
1
class
Point
{
2
private
:
3
int
x
;
4
int
y
;
5
char
name
;
6
7
public
:
8
Point
();
9
Point
(int
x
, int
y
, char
c
=
’P’
);
10
Point
(const
Point
&
p
);
11
12
int
getX
() { return
x
; }
13
int
getY
() { return
y
; }
14
char
getName
() { return
name
; }
15
void
setX
(int
i
) {
x
=
i
; }
16
void
setY
(int
i
) {
y
=
i
; }
17
void
setName
(char
c
) {
name
=
c
; }
18
double
distance
();
19
};
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
17/42
Konstruktory – przykład
1
Point
::
Point
()
2
{
3
x
=
y
= 0;
4
name
=
’P’
;
5
}
6
7
Point
::
Point
(int
x
, int
y
, char
c
)
8
{
9
this
->
x
=
x
;
10
this
->
y
=
y
;
11
name
=
c
;
12
}
13
14
Point
::
Point
(const
Point
&
p
)
15
{
16
x
=
p
.
x
;
17
y
=
p
.
y
;
18
name
=
p
.
name
;
19
}
20
21
Point p1
(10, 10,
’A’
);
// lub Point p1 = Point(10, 10, ’A’);
22
Point p2
=
Point
();
// Point p2(); to nie to samo
23
Point p3
;
24
Point p4
(
p1
);
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
18/42
Cechy konstruktorów
– Konstruktor mo˙ze by´c przeładowany.
– Konstruktor nie ma wyspecyfikowanego ˙zadnego typu warto´sci zwracanej, nie
zwraca nic - nawet typu void!
– Nie mo˙zna posłu˙zy´c si˛e adresem konstruktora.
– Konstruktor jest wywoływany przy tworzeniu obiektów chwilowych.
– W przypadku obiektów globalnych, których zakres wa˙zno´sci obejmuje cały
plik, konstruktor takiego obiektu jest uruchamiany na samym pocz ˛
atku, jeszcze
przed uruchomieniem funkcji głównej (main).
– Konstruktory i destruktory nie s ˛
a dziedziczone.
– Parametrami formalnymi konstruktora nie mog ˛
a by´c obiekty własnej klasy.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
19/42
Konstruktory i destruktory – przykład
1
class
Vector
{
2
private
:
3
double *
data
;
4
int
size
;
5
6
public
:
7
Vector
();
8
Vector
(int
size
, int
ini
= 0);
9
Vector
(const
Vector
&
source
);
10
~
Vector
();
11
double
getItem
(int
i
);
12
int
setItem
(int
i
, double
x
);
13
int
getSize
();
14
};
− data : double*
− size : int
+ Vector()
+ Vector(size : int, ini : int = 0)
+ Vector(source : const Vector)
+ ~Vector()
+ getItem(i : int) : double
+ setItem(i : int, x : double) : int
+ getSize() : int
Vector
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
20/42
Konstruktor domniemany
Konstruktor domniemany jest to konstruktor, który mo˙ze by´c wywołany bez
argumentów. W klasie mo˙ze istnie´c tylko jeden konstruktor domniemany. Mo˙ze by´c
to konstruktor nie maj ˛
acy argumentów lub maj ˛
acy wszystkie argumenty
domniemane. Je´sli klasa nie ma ˙zadnego konstruktora, to wygenerowany zostanie
automatycznie konstruktor domniemany (public).
1
Vector
::
Vector
()
2
{
3
size
= 0;
4
data
= 0;
5
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
21/42
Konstruktor kopiuj ˛
acy (inicjalizator kopiuj ˛
acy)
Konstruktor kopiuj ˛
acy jest to konstruktor, który mo˙zna wywoła´c z jednym
argumentem, b˛ed ˛
acym referencj ˛
a do danej klasy.
Konstruktor kopiuj ˛
acy mo˙zna wywoła´c jawnie, gdy chcemy utworzy´c obiekt na wzór
innego obiektu danej klasy. Niejawne wywołanie konstruktora kopiuj ˛
acego nast˛epuje
gdy:
– Podczas przesłania argumentów do funkcji, je´sli argumentem funkcji jest obiekt
klasy x przesyłany przez warto´s´c.
– Podczas, gdy funkcja jako swój rezultat zwraca przez warto´s´c obiekt klasy x.
To, co stoi przy instrukcji return, staje si˛e wzorcem do inicjalizacji obiektu
chwilowego b˛ed ˛
acego warto´sci ˛
a tej funkcji. Ten chwilowy obiekt nie jest ju˙z
lokalny – jest widziany z zewn ˛
atrz, z zakresu z którego funkcj˛e wywołali´smy.
1
Vector
::
Vector
(const
Vector
&
source
)
2
{
3
this
->
size
=
source
.
size
;
4
data
= new double[
size
];
5
for
(int
i
= 0;
i
<
size
; ++
i
)
data
[
i
] =
source
.
data
[
i
];
6
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
22/42
Cechy destruktora
– Destruktor jako funkcja nie mo˙ze zwraca´c ˙zadnej warto´sci (nawet typu void).
– Destruktor nie jest wywoływany z ˙zadnymi argumentami. W zwi ˛
azku z tym nie
mo˙ze by´c tak˙ze przeładowany.
– Destruktor jest automatycznie wywoływany, gdy obiekt automatyczny lub
chwilowy wychodzi ze swojego zakresu wa˙zno´sci.
– Destruktor mo˙zna wywoła´c jawnie: obiekt.∼klasa();.
1
Vector
::~
Vector
()
2
{
3
if
(
data
) delete []
data
;
4
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
23/42
Rodzaje polimorfizmu
W´sród zdefiniowanych w j˛ezyku C++ operatorów wyst˛epuj ˛
a operatory
polimorficzne. Wyró˙zniamy dwa rodzaje polimorfizmu:
– Koercj˛e, gdy dopuszcza si˛e, ˙ze argumenty operatora mog ˛
a by´c mieszanych
typów. Ten rodzaj polimorfizmu jest charakterystyczny dla operatorów
arytmetycznych: +, -, *, /; np. operator + mo˙ze słu˙zy´c do dodania dwóch liczb
całkowitych, liczby całkowitej do zmiennopozycyjnej, itd.
– Przeci ˛
a˙zenie (przeładowanie) operatora, gdy ten sam symbol operatora stosuje
si˛e w operacjach nie zwi ˛
azanych semantycznie, np.: operatory inicjalizacji i
przypisania, oznaczane s ˛
a symbolem ’=’; symbole ’>>’ oraz ’<<’ zale˙znie od
kontekstu, s ˛
a bitowymi operatorami przesuni˛ecia lub operatorami
wprowadzania/wyprowadzania.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
24/42
Przeładowanie operatorów
W j˛ezyku C++ istnieje mechanizm, który pozwala tworzy´c nowe definicje dla
istniej ˛
acych operatorów. Ogólna posta´c definicji operatora-funkcji jest nast˛epuj ˛
aca:
typ klasa
::
operator@
(
argumenty
){
/* wykonywane operacje */
}
Słowo typ oznacza typ zwracany przez operator – funkcj˛e, słowo klasa jest nazw ˛
a
klasy, w której funkcja definiuj ˛
aca operator jest funkcj ˛
a składow ˛
a, dwa dwukropki
oznaczaj ˛
a operator zasi˛egu, za´s symbol ’@’ jest zast˛epowany przez symbol operatora
(np. =, ==, +, ++, new). Nazwa funkcji definiuj ˛
acej operator składa si˛e ze słowa
kluczowego operator i nast˛epuj ˛
acego po nim symbolu operatora; np. operator+.
Lista operatorów, które mog ˛
a by´c przeładowane
+
−
∗
/
%
∧
&
|
∼
!
=
<
>
+ =
− =
∗ =
/ =
% =
∧
=
& =
| =
<<
>>
>>=
<<=
==
! =
<=
>=
&&
||
++
−−
,
− > ∗
− >
new
delete
( )
[ ]
Operatory &, *, −, + mog ˛
a by´c przeładowane zarówno w swojej wersji jedno- jak
i dwuargumentowej. Nie mo˙zna wymy´sla´c i przeładowywa´c własnych operatorów.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
25/42
Przeładowanie operatorów – przykład
1
class
Vector
{
// vector.h
2
private
:
3
double *
data
;
4
int
size
;
5
6
public
:
7
Vector
();
8
Vector
(int
size
, int
ini
= 0);
9
Vector
(const
Vector
&
source
);
10
~
Vector
();
11
12
double
getItem
(int
i
);
13
int
setItem
(int
i
, double
x
);
14
int
getSize
() const { return
size
; }
15
16
Vector
& operator=(const
Vector
&
source
);
17
Vector
operator
+(const
Vector
&
x
);
18
Vector
operator
-();
19
friend double operator*(const
Vector
&
x
, const
Vector
&
y
);
20
};
21
22
ostream
& operator<<(
ostream
&
s
,
Vector
&
x
);
23
istream
& operator>>(
istream
&
s
,
Vector
&
x
);
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
26/42
Przeładowanie operatorów – przykład
1
Vector Vector
::operator+(const
Vector
&
x
)
2
{
3
if
(
x
.
size
==
size
)
4
{
5
Vector z
(
x
.
size
);
6
for
(int
i
= 0;
i
<
x
.
size
; ++
i
)
7
{
8
z
.
data
[
i
] =
x
.
data
[
i
] +
data
[
i
];
9
}
10
return
z
;
11
}
12
// else
13
Vector z
;
14
return
z
;
15
}
16
17
Vector Vector
::operator-()
18
{
19
Vector z
=
Vector
(*this);
20
for
(int
i
= 0;
i
<
z
.
size
; ++
i
)
z
.
data
[
i
] = -
z
.
data
[
i
];
21
return
z
;
22
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
27/42
Przeładowanie operatorów – przykład
1
double operator*(const
Vector
&
x
, const
Vector
&
y
)
2
{
3
if
(
x
.
size
==
y
.
size
)
4
{
5
double
s
= 0;
6
for
(int
i
= 0;
i
<
x
.
size
; ++
i
)
s
+=
x
.
data
[
i
] *
y
.
data
[
i
];
7
return
s
;
8
}
9
// else
10
return
-1.0;
11
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
28/42
Przeładowanie operatorów – przykład
1
ostream
& operator<<(
ostream
&
s
,
Vector
&
x
)
2
{
3
int
i
,
j
;
4
j
=
x
.
getSize
();
5
s
<<
"("
;
6
if
(
j
> 0)
// je´
sli s ˛
a współrz˛
edne
7
{
8
for
(
i
= 0;
i
< (
j
- 1); ++
i
)
s
<<
x
.
getItem
(
i
) <<
", "
;
9
s
<<
x
.
getItem
(
j
- 1);
10
}
11
s
<<
")"
;
12
return
s
;
13
}
14
15
istream
& operator>>(
istream
&
s
,
Vector
&
x
)
16
{
17
int
i
,
j
;
18
double
n
;
19
j
=
x
.
getSize
();
20
for
(
i
= 0;
i
<
j
; ++
i
)
21
{
22
s
>>
n
;
23
x
.
setItem
(
i
,
n
);
24
}
25
return
s
;
26
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
29/42
Własno´sci przeci ˛
a˙zonych operatorów
– Operator @ jest zawsze przeci ˛
a˙zany wzgl˛edem klasy, w której jest
zadeklarowana jego funkcja operator@. Zatem w innych kontekstach operator
nie traci ˙zadnego ze swych oryginalnych znacze´n, ustalonych w definicji j˛ezyka.
– Funkcja definiuj ˛
aca operator musi by´c funkcj ˛
a składow ˛
a klasy, albo mie´c co
najmniej jeden argument b˛ed ˛
acy obiektem lub referencj ˛
a do obiektu klasy (poza
funkcjami redefiniuj ˛
acymi operatory new i delete).
– Nie mog ˛
a by´c przeci ˛
a˙zane operatory ’.’, ’.*’, ’::’, ’?:’, ’sizeof’ oraz symbole ’#’
i ’##’.
– Nie jest mo˙zliwa zmiana priorytetu, reguł ł ˛
aczno´sci, ani liczby argumentów
operatora.
– Funkcje definiuj ˛
ace operatory, za wyj ˛
atkiem funkcji operator=(), s ˛
a
dziedziczone.
– Przeci ˛
a˙zany operator nie mo˙ze mie´c argumentów domy´slnych.
– Funkcje: operator=(), operator[](), operator()() i operator− >() nie mog ˛
a by´c
statycznymi funkcjami składowymi; dzi˛eki temu ich pierwsze od lewej
argumenty b˛ed ˛
a l-warto´sciami.
– Funkcja definiuj ˛
aca operator nie mo˙ze posługiwa´c si˛e wył ˛
acznie wska´znikami.
– Operatory: ’=’ (przypisania), ’&’ (pobrania adresu) i ’,’ (przecinkowy) maj ˛
a
predefiniowane znaczenie w odniesieniu do obiektów klas (o ile nie zostały na
nowo zdefiniowane).
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
30/42
Funkcje zaprzyja´znione
Funkcja operatorowa mo˙ze by´c zwykł ˛
a, globaln ˛
a funkcj ˛
a lub funkcj ˛
a składow ˛
a klasy.
Je´sli operator definiujemy jako funkcj˛e składow ˛
a, to ma ona o jeden argument mniej
ni˙z ta sama funkcja napisana w postaci globalnej (ze wzgl˛edu na wska´znik this).
Je˙zeli chcemy, by operator mógł pracowa´c na niepublicznych składnikach klasy, to
musimy zadeklarowa´c j ˛
a jako funkcj˛e zaprzyja´znion ˛
a z klas ˛
a, tzn. we wn˛etrzu
definicji klasy umieszczamy deklaracj˛e tej funkcji poprzedzon ˛
a słowem kluczowym
friend. Funkcja zaprzyja´zniona ma dost˛ep do wszystkich, nawet prywatnych
składników klasy. Funkcja zaprzyja´zniona nie jest składnikiem klasy i nie zawiera
ukrytego wska´znika this. Zasi˛eg funkcji zaprzyja´znionej jest inny ni˙z funkcji
składowych klasy, jest ona widziana w zasi˛egu zewn˛etrznym, tak jak klasa, w której
została zadeklarowana.
1
double operator*(const
Vector
&
x
, const
Vector
&
y
)
2
{
3
if
(
x
.
size
==
y
.
size
)
4
{
5
double
s
= 0;
6
for
(int
i
= 0;
i
<
x
.
size
; ++
i
)
s
+=
x
.
data
[
i
] *
y
.
data
[
i
];
7
return
s
;
8
}
9
// else
10
return
-1.0;
11
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
31/42
Operator przypisania
Inicjalizacj ˛
a zajmuje si˛e konstruktor kopiuj ˛
acy, a przypisaniem operator przypisania.
Jedyn ˛
a sytuacj ˛
a, gdy na widok znaczka ’=’ rusza do pracy konstruktor kopiuj ˛
acy, jest
wyst ˛
apienie tego znaku w linijce definicji obiektu.
Operator przypisania składa si˛e zwykle z dwóch cz˛e´sci. Najpierw nast˛epuje cz˛e´s´c
destruktorowa, po czym nast˛epuje cz˛e´s´c konstruktorowa przypominaj ˛
aca konstruktor
kopiuj ˛
acy. Ostatnia instrukcja oznacza, ˙ze operator przypisania zwraca jako rezultat
referencj˛e do obiektu, który stoi po lewej stronie operatora przypisania. Umo˙zliwia
to kaskadowe ł ˛
aczenie operatora przypisania, np.: a = b = c;
Vector
&
Vector
::operator=(
Vector
&
s
)
Operator przypisania mo˙ze jako argument przyjmowa´c obiekt danej klasy przysłany
przez warto´s´c lub przez referencj˛e.
Je˙zeli takiego operatora nie zdefiniujemy wówczas kompilator postara si˛e o
wygenerowanie swojego operatora przypisania, przypisuj ˛
acego na zasadzie „składnik
po składniku”. S ˛
a sytuacje, gdy automatyczne generowanie mo˙ze si˛e okaza´c
niemo˙zliwe:
– Je˙zeli klasa ma składnik const.
– Je˙zeli klasa ma składnik b˛ed ˛
acy referencj ˛
a.
– Je´sli klasa ma składnik b˛ed ˛
acy obiektem innej klasy i w tej innej klasie operator
przypisania okre´slony jest jako private.
– Je˙zeli klasa ma klas˛e podstawow ˛
a, w której operator= jest typu private.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
32/42
Operator przypisania – przykład
1
Vector
&
Vector
::operator=(const
Vector
&
source
)
2
{
3
if
(this != &
source
)
// sprawdzamy, czy nie ma przypisania x = x
4
{
5
delete
[]
data
;
6
size
=
source
.
size
;
7
data
= new double[
size
];
8
for
(int
i
= 0;
i
<
size
; ++
i
)
data
[
i
] =
source
.
data
[
i
];
9
}
10
return *this;
// operator przypisania zwraca referencj˛
e do obiektu
11
// dla którego został wywołany
12
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
33/42
Przeładowanie operatorów – przykład
1
int
main
()
// vectormain.cpp
2
{
3
int
i
;
4
cout
<<
"Podaj rozmiar wektora a: "
;
5
cin
>>
i
;
6
Vector a
=
Vector
(
i
);
7
cout
<<
"Po utworzeniu obiektu\na: "
<<
a
<<
endl
;
8
cout
<<
"Podaj współrz˛
edne: "
;
9
cin
>>
a
;
10
cout
<<
"Po wczytaniu danych\na: "
<<
a
<<
endl
;
11
Vector b
;
12
cout
<<
"Po utworzeniu obiektu\nb: "
<<
b
<<
endl
;
13
b
=
a
;
14
cout
<<
"Po przypisaniu\nb: "
<<
b
<<
endl
;
15
Vector c
=
Vector
(
a
);
16
cout
<<
"Po utworzeniu obiektu\nc: "
<<
c
<<
endl
;
17
c
=
a
+
b
;
18
cout
<<
"Po obliczeniu sumy\nc: "
<<
c
<<
endl
;
19
c
= -
c
;
20
cout
<<
"Po zmianie znaku\nc: "
<<
c
<<
endl
;
21
Vector d
(
i
+ 1, 2);
22
cout
<<
"Po utworzeniu obiektu\nd: "
<<
d
<<
endl
;
23
cout
<<
"Iloczyn skalarny a * b: "
<<
a
*
b
<<
endl
;
24
cout
<<
"Iloczyn skalarny a * d: "
<<
a
*
d
<<
endl
;
25
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
34/42
Przeładowanie operatorów – podsumowanie
Operatory =, [], (), − > mog ˛
a by´c przeładowane tylko jako niestatyczne funkcje
składowe klasy. W pozostałych przypadkach mo˙zna wybra´c jedn ˛
a z dwóch wersji
przeładowania.
Je´sli operator zmienia obiekt, na którym pracuje, to definiujemy go jako funkcj˛e
składow ˛
a klasy, w innym przypadku jako funkcj˛e globaln ˛
a (ewentualnie
zaprzyja´znion ˛
a z klas ˛
a).
friend
Vector
operator*(int
x
, const
Vector
&
y
);
Je˙zeli chcemy, by w powy˙zszym przykładzie mogła zachodzi´c przemienno´s´c
działania, to musi zdefiniowa´c dwie funkcje globalne o identycznych ciałach:
friend
Vector
operator*(int
x
, const
Vector
&
y
);
friend
Vector
operator*(const
Vector
&
y
, int
x
);
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
35/42
Konwersje
Konwersje jest to dopasowanie typu zmiennej w sytuacji, gdy nie jest on dokładnie
taki jakiego si˛e spodziewano. Konwersje s ˛
a wykonywane niejawnie, gdy:
– W wywołaniu funkcji wyst˛epuje niezgodno´s´c mi˛edzy argumentami formalnymi
i aktualnymi i istnieje jednoznaczna mo˙zliwo´s´c dopasowania argumentów
aktualnych z u˙zyciem konwersji.
– Przy zwracaniu wyniku przez funkcj˛e, gdy warto´s´c w instrukcji return jest
innego typu ni˙z wynika to z definicji funkcji.
– Przy u˙zyciu operatorów, gdy nast˛epuje niezgodno´s´c argumentów.
– W instrukcjach steruj ˛
acych (if, switch, while,...), gdy w wyra˙zeniu umieszczono
warto´sci innego typu ni˙z int.
– W wyra˙zeniach inicjalizuj ˛
acych.
Konwersje mo˙zna wywoła´c równie˙z jawnie. Istniej ˛
a dwie formy zapisu wywołania
konwersji:
– „wywołanie funkcji”, np. n = int(x);
– „rzutowanie”, np. n = (int)x;
Mo˙zliwe jest definiowanie własnych funkcji konwersji dla nowobudowanych klas.
Konwersje mo˙zna zbudowa´c na dwa sposoby: jako konstruktor jednoargumentowy
przekształcaj ˛
acy typ argumentu, na typ własnej klasy oraz jako operator konwersji,
przekształcaj ˛
acy typ klasy do której nale˙zy na inny typ danych.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
36/42
Konwersje – przykład
1
class
Fraction
{
// fraction.h (fraction.cpp, fractionmain.cpp)
2
private
:
3
int
numerator
;
4
int
denominator
;
5
6
void
abridge
();
7
void
sign
();
8
public
:
9
Fraction
();
10
Fraction
(int
a
, int
b
);
11
Fraction
(const
Fraction
&
x
);
12
Fraction
(int
a
);
// ten konstruktor mo˙
zna u˙
zy´
c do konwersji
13
14
Fraction
operator*(const
Fraction
&
x
);
15
Fraction
operator
/(const
Fraction
&
x
);
16
17
friend
std
::
ostream
& operator<<(
std
::
ostream
&
s
,
Fraction
&
x
);
18
friend
std
::
istream
& operator>>(
std
::
istream
&
s
,
Fraction
&
x
);
19
friend
Fraction
operator
+(const
Fraction
&
x
, const
Fraction
&
y
);
20
friend
Fraction
operator
-(const
Fraction
&
x
, const
Fraction
&
y
);
21
};
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
37/42
Konwersje – przykład
1
int
main
()
// fractionmain2.cpp
2
{
3
Fraction u
,
u1
,
u2
;
4
int
i
,
j
;
5
6
cout
<<
"Podaj licznik i mianownik ułamka: "
;
7
cin
>>
u1
;
8
cout
<<
"Podaj licznik i mianownik ułamka: "
;
9
cin
>>
u2
;
10
cout
<<
"Podaj liczb˛
e całkowit ˛
a i: "
;
11
cin
>>
i
;
12
cout
<<
"Podaj liczb˛
e całkowit ˛
a j: "
;
13
cin
>>
j
;
14
15
cout
<<
"i + j: "
<<
i
+
j
<<
endl
;
16
u
=
u1
+
u2
;
17
cout
<<
"u1 + u2: "
<<
u
<<
endl
;
18
u
=
u1
+
i
;
19
cout
<<
"u1 + i: "
<<
u
<<
endl
;
20
u
=
i
+
u2
;
21
cout
<<
"i + u2: "
<<
u
<<
endl
;
22
return
0;
23
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
38/42
Operator konwersji
Deklaracja wewn ˛
atrz klasy
operator float
();
Definicja funkcji konwertuj ˛
acej
1
punkt
::operator float()
2
{
3
return
x
;
4
}
Je˙zeli chcemy dokona´c konwersji na typ wbudowany, to jedyn ˛
a mo˙zliwo´sci ˛
a jest
zdefiniowanie operatora konwersji (Operator konwersji jest dziedziczony).
Je˙zeli chcemy dokona´c konwersji z klasy A na klas˛e B, to staramy napisa´c si˛e
operator konwersji operator B(); w klasie A, a je´sli jest ona niedost˛epna, to
konstruktor konwertuj ˛
acy w klasie B przyjmuj ˛
acy jako argument obiekt klasy A.
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
39/42
Operator konwersji – przykład
1
class
Fraction
{
2
private
:
3
int
numerator
;
4
int
denominator
;
5
6
void
abridge
();
7
void
sign
();
8
public
:
9
Fraction
();
10
Fraction
(int
a
, int
b
);
11
Fraction
(const
Fraction
&
x
);
12
explicit
Fraction
(int
a
);
13
14
Fraction
operator*(const
Fraction
&
x
);
15
Fraction
operator
/(const
Fraction
&
x
);
16
operator int
();
// operator rzutowania na typ int
17
18
friend
std
::
ostream
& operator<<(
std
::
ostream
&
s
,
Fraction
&
x
);
19
friend
std
::
istream
& operator>>(
std
::
istream
&
s
,
Fraction
&
x
);
20
friend
Fraction
operator
+(const
Fraction
&
x
, const
Fraction
&
y
);
21
friend
Fraction
operator
-(const
Fraction
&
x
, const
Fraction
&
y
);
22
};
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
40/42
Operator konwersji – przykład
1
int
main
()
2
{
3
Fraction u1
,
u2
;
4
int
i
,
j
;
5
6
cout
<<
"Podaj licznik i mianownik ułamka: "
;
7
cin
>>
u1
;
8
cout
<<
"Podaj licznik i mianownik ułamka: "
;
9
cin
>>
u2
;
10
cout
<<
"Podaj liczb˛
e całkowit ˛
a i: "
;
11
cin
>>
i
;
12
13
j
= int(
u1
) +
i
;
14
cout
<<
"u1 + i: "
<<
j
<<
endl
;
15
16
j
=
i
+ int(
u2
);
17
cout
<<
"i + u2: "
<<
j
<<
endl
;
18
return
0;
19
}
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
41/42
Składowe statyczne
Składowe statyczne poprzedza si˛e podczas definicji słówkiem static. Statyczne mog ˛
a
by´c zarówno funkcje, jak i pola nale˙z ˛
ace do klasy. Składnik statyczny jest
elementem, który jest powi ˛
azany z klas ˛
a, a nie z obiektem tej klasy. Funkcje
statyczne słu˙z ˛
a do operowania na składnikach statycznych.
1
class
Date
{
2
int
d
,
m
,
y
;
3
static
Date startDate
;
4
public
:
5
Date
(int
d
= 0; int
m
= 0; int
y
= 0);
6
//...
7
static void
setStartDate
(int, int, int);
8
};
9
10
Date
::
Date
(int
d
= 0; int
m
= 0; int
y
= 0)
11
{
12
this
->
d
=
d
?
d
:
startDate
.
d
;
13
this
->
m
=
m
?
m
:
startDate
.
m
;
14
this
->
y
=
y
?
y
:
startDate
.
y
;
15
}
16
17
Date
::
setStartDate
(1,1,2008);
18
myDate
.
setStartDate
(1,1,2008);
Marcin Szpyrka
J˛ezyki programowania – cz˛e´s´c V
42/42