float

Transkrypt

float
Klasa, metody, rozwijanie w linii
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
˛
w całości, razem z
warunkiem wykorzystania wyłacznie
˛
do własnych prywatnych potrzeb i może on być kopiowany wyłacznie
niniejsza˛ strona˛ tytułowa.
˛
1
Metody klasy
Jeszcze funkcja
struct LiczbaZespolona {
float re;
float im;
};
Już metoda
struct LiczbaZespolona {
float re;
float im;
void Sprzezenie( );
};
void sprzezenie( LiczbaZespolona ∗wLZesp )
{
wLZesp–>im = – wLZesp–>im;
}
void LiczbaZespolona::Sprzezenie( )
{
im = – im;
}
int main( )
{
LiczbaZespolona
int main( )
{
LiczbaZespolona
LZesp;
LZesp.re = 5; LZesp.im = -6;
sprzezenie(&LZesp);
}
LZesp;
LZesp.re = 5; LZesp.im = -6;
LZesp.Sprzezenie( );
}
Klasa, metody, rozwijanie w linii
2
Metody klasy
Struktura i funkcja
Struktura i metoda
Klasa, metody, rozwijanie w linii
3
Metody klasy
Jeszcze funkcja
struct LiczbaZespolona {
float re;
float im;
};
Już metoda
struct LiczbaZespolona {
float re;
float im;
void Zmien( float r, float i );
};
void zmien(LiczbaZespolona ∗wArg, float r,
float i )
{
wArg->re = r; wArg->im = i;
}
void LiczbaZespolona::Zmien(float r, float i)
{
re = r; im = i;
}
int main( )
{
LiczbaZespolona
int main( )
{
LiczbaZespolona
LZesp;
LZesp.Zmien(5, -6);
zmien(&LZesp, 5, -6);
}
LZesp;
}
Klasa, metody, rozwijanie w linii
4
Metody klasy - wartości domyślne
struct LiczbaZespolona {
float re;
float im;
void Zwieksz( float r = 1, float i = 1 );
};
void LiczbaZespolona::Zwieksz( float r, float i )
{
re += r; im += i;
}
int main( )
{
LiczbaZespolona
}
LZesp;
LZesp.Zwieksz( );
LZesp.Zwieksz( 2 );
LZesp.Zwieksz( 1, -1 );
Wartości domyślne metod sa˛ tak samo traktowane jak wartości domyślne funkcji.
Klasa, metody, rozwijanie w linii
5
Metody klasy - wartości domyślne
struct LiczbaZespolona {
float re;
float im;
void Zwieksz( float r = 1, float i = 1 );
};
(
(
hh(
hh(
(
h(
h(
(
h
hh
h float i ((
void LiczbaZespolona::Zwieksz( float r (
=h1,
=h1(
)
{
re += r; im += i;
}
int main( )
{
LiczbaZespolona
}
LZesp;
LZesp.Zwieksz( );
LZesp.Zwieksz( 2 );
LZesp.Zwieksz( 1, -1 );
Wartości domyślne moga˛ wystapić
˛ tylko raz w deklaracji metody w ciele definicji struktury/klasy.
Klasa, metody, rozwijanie w linii
6
Metody klasy - wartości domyślne
struct LiczbaZespolona {
float re;
float im;
void Zmien( float r, float i );
void Zmien( float ri );
};
void LiczbaZespolona::Zmien( float r, float i )
{
re = r im = i;
}
void LiczbaZespolona::Zmien( float ri )
{
re = im = ri;
}
int main( )
{
LiczbaZespolona
}
LZesp;
LZesp.Zmien( 2 );
LZesp.Zmien( 1, -1 );
Podobnie jak zwykłe funkcje można również przeciażać
˛
metody.
Klasa, metody, rozwijanie w linii
7
Metody klasy - wartości domyślne
struct LiczbaZespolona {
float re;
float im;
h(
h
(
h
h
void Zmien( float r, float i (h
=(
0(
);
void Zmien( float ri );
h
(
};
void LiczbaZespolona::Zmien( float r, float i )
{
re = r im = i;
}
void LiczbaZespolona::Zmien( float ri )
{
re = im = ri;
}
int main( )
{
LiczbaZespolona
}
LZesp;
LZesp.Zmien( 2 );
LZesp.Zmien( 1, -1 );
Również i w tym przypadku konieczne jest zadbanie o jednoznaczność przeciaże
˛ ń.
Klasa, metody, rozwijanie w linii
8
Metody klasy
Jeszcze funkcja
struct LZespolona {
float re;
float im;
};
Już metoda
struct LZespolona {
float re;
float im;
LZespolona Dodaj( LZespolona Z2 );
};
LZespolona dodaj( LZespolona Z1,
LZespolona Z2 )
{
Z2.re += Z1.re; Z2.im += Z1.im;
return Z2;
}
LZespolona LZespolona::Dodaj( LZespolona Z2 )
{
Z2.re += re; Z2.im += im;
return Z2;
}
int main( )
{
LZespolona
int main( )
{
LZespolona
lz1, lz2;
lz2 = dodaj( lz1, lz2 );
}
lz1, lz2;
lz2 = lz1.Dodaj( lz2 );
}
Klasa, metody, rozwijanie w linii
9
Metody klasy
Jeszcze funkcja
struct LZespolona {
float re;
float im;
};
Już metoda
struct LZespolona {
float re;
float im;
LZespolona operator + ( LZespolona Z );
};
LZespolona operator + ( LZespolona Z1,
LZespolona Z2 )
{
Z2.re += Z1.re; Z2.im += Z1.im;
return Z2;
}
int main( )
{
LZespolona
lz2 = lz1 + lz2;
}
lz1, lz2;
LZespolona LZespolona::operator + ( LZespolona Z2 )
{
Z2.re += re; Z2.im += im;
return Z2;
}
int main( )
{
LZespolona
lz1, lz2;
lz2 = lz1 + lz2;
}
Klasa, metody, rozwijanie w linii
10
Równoważność definicji
struct
class
struct LZespolona {
float re;
float im;
void Sprzezenie( );
void Zmien( float r, float i );
LZespolona operator + ( LZespolona Z );
class LZespolona {
public:
float re;
float im;
=
void Sprzezenie( );
void Zmien( float r, float i );
LZespolona operator + ( LZespolona Z );
};
};
struct NazwaKlasy {
...
};
class NazwaKlasy {
public:
=
...
};
Klasa, metody, rozwijanie w linii
11
Reprezentacja w UML
C++
UML
Klasa
class LZespolona {
public:
float re;
float im;
void Sprzezenie( );
void Zmien( float r, float i );
LZespolona operator + ( LZespolona Z );
};
Klasa, metody, rozwijanie w linii
11
Reprezentacja w UML
C++
UML
Klasa
class LZespolona {
public:
float re;
float im;
void Sprzezenie( );
void Zmien( float r, float i );
LZespolona operator + ( LZespolona Z );
};
Obiekt
LZespolona
Arg2;
Klasa, metody, rozwijanie w linii
12
Konstruktory i destruktory
class LZespolona { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
float re, im;
LZespolona( ) { re = im = 0; }
LZespolona( float r, float i ) { re = r; im = i; }
// Konstruktor
// Konstruktor
˜LZespolona( ) { }
// Destruktor
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
LZespolona
LZespolona
...
}
Z1;
Z2(2,0);
Oprócz zwykłych metod można zdefiniować w każdej klasie specjalne metody nazywane konstruktorami i destruktorami. Sa˛ one wywoływane niejawnie odpowiednio w momencie tworzenia i destrukcji
obiektu. Konstruktory można przeciażać,
˛
jak też stosować argumenty domyślne. Destruktor jest
zawsze tylko jeden (bezparametryczny). Wywołanie odpowiedniego konstruktora można jawnie wymusić w deklaracji zmiennej. Destruktory maja˛ istotne znaczenie w przypadku wystepowania
˛
pól
wskaźnikowych.
Klasa, metody, rozwijanie w linii
13
Obiekty jako pola
class Wektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
float x, y;
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
class Prostokat { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
Wektor Rog GornyLewy, Rog DolnyPrawy;
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
int main( )
{
Prostokat
Pr;
Pr.Rog GornyLewy.x = 0;
Pr.Rog GornyLewy.y = 100;
Pr.Rog DolnyPrawy.x = 100; Pr.Rog DolnyPrawy.y = 1;
}
Obiekty danej klasy moga˛ być polami obiektów innej klasy. Ich konstrukcja jest analogiczna jak
konstrukcja struktur w jezyku
˛
C z wykorzystaniem pól bed
˛ acych
˛
również strukturami.
Klasa, metody, rozwijanie w linii
14
Reprezentacja zależności
class Wektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
float x, y;
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
class Prostokat { //. . . . . . . . . . . . . . . . . . . . . . . . . . . .
public:
Wektor Rog GornyLewy;
Wektor Rog DolnyPrawy;
}; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Wykorzystujac
˛ diagram klas rysowany w jezyku
˛
UML można graficznie przedstawić zależności mie˛
dzy klasami. Pozwala to określić, czy zmiany w danej klasie moga˛ skutkować koniecznościa˛ zmian
również w innych klasach. Takie informacje bezpośrednio z kodu programu sa˛ trudne do odczytania.
Klasa, metody, rozwijanie w linii
15
Metody rozwijane w linii
class LZespolona {
public:
float re;
float im;
class LZespolona {
public:
float re;
float im;
inline void Sprzezenie( );
void Sprzezenie( )
{
im = – im;
}
};
inline void LZespolona::Sprzezenie( )
{
im = – im;
}
};
int main( )
{
LiczbaZespolona
LZesp;
int main( )
{
LiczbaZespolona
LZesp.Sprzezenie( );
}
LZesp;
LZesp.Sprzezenie( );
}
Specyfikator inline umożliwia rozwiniecie
˛
kodu metod i przeciaże
˛ ń operatorów w miejscu ich wywołania. Metody i przeciażenia
˛
operatorów definiowane w ciele klasy sa˛ domyślnie rozwijane w linii, o
ile nie zostanie to zmienione za pomoca˛ opcji kompilatora, np -fno-default-inline dla g++.
Klasa, metody, rozwijanie w linii
16
Metody rozwijane w linii
class LZespolona {
public:
float re;
float im;
void Sprzezenie( )
{
im = – im;
}
};
class LZespolona {
public:
void Sprzezenie( )
{
im = – im;
}
float re;
float im;
};
Definicja metody lub przeciażenie
˛
operatora rozwijane w linii w ciele definicji klasy może znajdować
sie˛ zarówno przed, jak też po deklaracji pól danej klasy. Nie jest wówczas błedem
˛
odwoływanie sie˛
do pól przed ich formalnym zadeklarowaniem.
Klasa, metody, rozwijanie w linii
17
Funkcje rozwijane w linii
inline double poteguj( double Wykladnik, unsigned int Potega )
{
...
}
Tak jak metody, funkcje oraz przeciażenia
˛
operatorów również moga˛ być rozwijane w miejscu ich
wywołania.
inline double poteguj( double Wykladnik, unsigned int Potega )
{
return (Potega == 0) ? 1 : Wykladnik∗poteguj(Potega-1);
}
...
Wynik = poteguj(4,3);
Sposób rozwiniecia
˛
wywołania funkcji zależy od inteligencji kompilatora. Dla wywołania przedstawionego powyżej rozwiniecie
˛
kodu może sprowadzić sie˛ do wstawienia wartości 64. W innym
przypadku rozwiniecie
˛
może mieć postać: 4∗potega(2);
Klasa, metody, rozwijanie w linii
18
Metody rozwijane w linii
plik1.cpp
inline
double poteguj( double, unsigned int);
plik2.cpp
inline
double poteguj( double w, unsigned int p )
{
return (p == 0) ? 1 : w∗poteguj(p - 1);
}
int main( )
{
double Wynik = poteguj(12, 3);
}
Funkcja rozwijana w linii wywołania powinna wystepować
˛
w tej samej jednostce translacyjnej, w
której wystepuje
˛
jej wywołanie. Kompilator może dysponować dodatkowymi udogodnieniami pozwalajacymi
˛
na umieszczenie funkcji w innej jednostce. Jednak nie jest to reguła. Aby zagwarantować
przenośność należy nie uwzgledniać
˛
tych dodatkowych udogodnień.
Nie jest konieczne natomiast, aby definicja wystepowała
˛
przed jej wywołaniem. Jednak jest to zalecane (nie każdy kompilator może sobie z tym poradzić).
Klasa, metody, rozwijanie w linii
19
Metody rozwijane w linii
plik1.cpp
plik2.cpp
// Ta definicja funkcji zawiera bład.
˛
inline
double poteguj( double w, unsigned int p );
{
double rezultat = 0;
for ( ; p ; −−p) rezultat = rezultat∗w;
return w;
}
inline
double poteguj( double w, unsigned int p )
{
return (p == 0) ? 1 : w∗poteguj(p - 1);
}
Jeżeli funkcja rozwijana w linii wystepuje
˛
pod ta˛ sama˛ nazwa˛ w kilku jednostkach translacyjnych, to
jej definicja powinna być taka sama.
Przykład powyżej prezentuje błedne
˛
realizacje˛ implementacji tej funkcji. W jednym z plików funkcja
ta zawiera bład.
˛ Tego rodzaju pomyłki sa˛ bardzo trudne do debugowia (wydaje sie,
˛ że funkcja raz
działa poprawnie, a raz nie).
Z tego powodu zalecane jest umieszczanie definicji tego rodzaju funkcji w plikach nagłówkowych.
Klasa, metody, rozwijanie w linii
20
Metody rozwijane - ważniejsze uwagi
• Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia
˛
funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które
powinno być w miare˛ możliwości uwzglednione.
˛
Klasa, metody, rozwijanie w linii
20
Metody rozwijane - ważniejsze uwagi
• Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia
˛
funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które
powinno być w miare˛ możliwości uwzglednione.
˛
• Kompilator przy podejmowaniu decyzji o rozwinieciu
˛
kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj
˛
acymi,
˛
np. rozmiar
kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć
˛ w aktualnie kompilowanej funkcji.
Klasa, metody, rozwijanie w linii
20
Metody rozwijane - ważniejsze uwagi
• Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia
˛
funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które
powinno być w miare˛ możliwości uwzglednione.
˛
• Kompilator przy podejmowaniu decyzji o rozwinieciu
˛
kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj
˛
acymi,
˛
np. rozmiar
kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć
˛ w aktualnie kompilowanej funkcji.
• Rozwiniecie
˛
funkcji nie zawsze musi oznaczać zwiekszenie
˛
rozmiaru
kodu (aczkolwiek zazwyczaj tak jest).
Klasa, metody, rozwijanie w linii
20
Metody rozwijane - ważniejsze uwagi
• Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia
˛
funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które
powinno być w miare˛ możliwości uwzglednione.
˛
• Kompilator przy podejmowaniu decyzji o rozwinieciu
˛
kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj
˛
acymi,
˛
np. rozmiar
kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć
˛ w aktualnie kompilowanej funkcji.
• Rozwiniecie
˛
funkcji nie zawsze musi oznaczać zwiekszenie
˛
rozmiaru
kodu (aczkolwiek zazwyczaj tak jest).
• Stosowanie cz˛estych rozwinieć
˛ funkcji nie zawsze musi oznaczać przyśpieszenie wykonywania kodu (aczkolwiek zazwyczaj tak jest).
Klasa, metody, rozwijanie w linii
20
Metody rozwijane - ważniejsze uwagi
• Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia
˛
funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które
powinno być w miare˛ możliwości uwzglednione.
˛
• Kompilator przy podejmowaniu decyzji o rozwinieciu
˛
kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj
˛
acymi,
˛
np. rozmiar
kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć
˛ w aktualnie kompilowanej funkcji.
• Rozwiniecie
˛
funkcji nie zawsze musi oznaczać zwiekszenie
˛
rozmiaru
kodu (aczkolwiek zazwyczaj tak jest).
• Stosowanie cz˛estych rozwinieć
˛ funkcji nie zawsze musi oznaczać przyśpieszenie wykonywania kodu (aczkolwiek zazwyczaj tak jest).
• Specyfikator inline nie wpływa na znaczenie funkcji; funkcja rozwijana w
miejscu wywołania nadal ma unikatowy adres.
Klasa, metody, rozwijanie w linii
21
Pytania i ćwiczenia
1. Czy funkcja main może być rozwijana w linii?
2. Jeżeli w funkcji wystepuj
˛ a˛ zmienne statyczne, to czy można do takiej funkcji zastosować specyfikator inline?
3. Czy w danej funkcji, do której zastosowano specyfikator inline można
wywołać inna˛ funkcje, do której również zastosowano ten specyfikator?
Jakie to bedzie
˛
miało konsekwencje dla postaci kodu?
Klasa, metody, rozwijanie w linii