Programowanie obiektowe, wykład nr 8 Konstruktory i destruktory

Transkrypt

Programowanie obiektowe, wykład nr 8 Konstruktory i destruktory
Dr hab. inż. Lucyna Leniowska, prof. UR, Zakład Mechatroniki, Automatyki i Optoelektroniki, IT
Programowanie obiektowe, wykład nr 8
Konstruktory i destruktory – c.d.
Przeciążanie operatorów
Zestawienie wiadomości o konstruktorach
Przykład 8.1.
class wektor2d
{
public:
double x, y;
};
Gdy piszemy:
wektor2d wynik;
wynik.x = 1; wynik.y = 2;
to pole x w obiekcie przechowywanym w zmiennej wynik zostanie
zainicjalizowane dwa razy:
 pierwszy raz na 0,
 drugi raz na 1.
Z tego powodu warto używad konstruktorów z listą inicjalizacyjną, bo wtedy
pola klasy inicjalizowane są tylko raz.
1. Konstruktor wieloargumentowy
Służy do zainicjowania obiektów danej klasy, dla których jest możliwe
podanie wszystkich wartości składowych.
Przykład 8.2.
class wektor2d
{
public:
double x, y;
wektor2d(double nowyX, double nowyY) { x = nowyX;
y = nowyY; }
double dlugosc() const {
return sqrt(x*x + y*y);
}
}
Wywołanie konstruktora:
int main()
{
wektor2d p(2, 5);
return 0;
}
2. Konstruktor inicjujący wartościami domyślnymi
Umożliwia inicjowanie składowych klasy ustalonymi ‘z góry’ wartościami.
Najczęściej służy do zainicjowania tablic obiektów danej klasy, dla których nie
jest możliwe podanie wszystkich wartości.
Przykład 8.3.
class wektor2d
{
public:
double x, y;
wektor2d(double x=4, double y=5) {this->x=x; this->y=y;}
double dlugosc() const {
return sqrt(x*x + y*y);
}
}
Wywołanie konstruktora:
int main(void) {
wektor2d p1[10], p2;
return 0;
}
3. Konstruktor kopiujący
Jako argument otrzymuje tylko obiekt danej klasy. Jego działanie polega na
kopiowaniu wartości pól klasy, czyli tworzeniu kopii obiektu.
Przykład 8.4.
class wektor2d
{
public:
double x, y;
//konstruktor kopiujący
wektor2d(wektor2d &wkt) {x=wkt.x; y=wkt.y;}
double dlugosc() const {
return sqrt(x*x + y*y);
}
Wywołanie konstruktora:
int main(void) {
wektor2d a(1,2), b(3,4); //zainicjowane konstruktorem parametrycznym
wektor2d Kopia = a;
wektor2d w(b);
}
4. Konstruktory z listą inicjalizacyjną
Istnieje możliwośd zdefiniowania konstruktora z tzw. listą inicjalizacyjną:
o Konstruktor bez parametrów:
wektor2d() : x(0), y(0) { }
o Konstruktor z dwoma parametrami i listą:
wektor2d(double nowyX, double nowyY) : x(nowyX),
y(nowyY) { }
 Dzięki listom inicjalizacyjnym, które wywołują konstruktory dla składowych
klasy możemy uniknąd podwójnego nadpisywania pól klasy, tzn.
lepiej napisad:
wektor2d(double nx, double ny) : x(nx), y(ny) { }
niż
wektor2d(double nx, double ny) { x = nx; y = ny; }
 Inicjalizacja z użyciem konstruktora z listą:
wektor2d a(4, 6), b(1, 2);
Obiekty bez nazwy
 Obiekty bez nazwy tworzymy zgodnie ze schematem:
klasa(parametry_konstruktora)
np. wektor2d(1, 2);
 Obiekty bez nazwy pozwalają na efektywniejsze zwracanie parametrów.
Aby zwrócid więcej niż jedną wartośd na raz można np. ‘zamknąd’ wszystkie
wartości, które chcemy zwrócid w strukturę lub klasę.
 Dlatego możemy przerobid funkcję negacja (przykł z poprzedniego
wykładu):
void wektor2d::negacja() {
x = -x;
y = -y;
}
na taką, która zamiast zmieniad obiekt, na rzecz którego została wywołana,
tworzy nowy, już zanegowany wektor (obiekt):
wektor2d negacja() const {
return wektor2d(-x, -y);
}
 Pozwala to później napisad wywołanie: a.suma(b.negacja()), w
którym wynik zwracany przed funkcję (typu wektor2d) jest parametrem
innej funkcji.
Przykład 8.5.
Porównanie implementacji wektora na płaszczyźnie (przykład z poprzedniego
wykładu) za pomocą klasy z dwoma konstruktorami i klasy bez konstruktorów.
//bez konstruktora
#include <cmath>
#include <iostream>
//z konstruktorami
#include <cmath>
#include <iostream>
class wektor2d {
public:
double x;
double y;
class wektor2d {
public:
double x;
double y;
wektor2d() : x(0), y(0) { }
wektor2d(double nowyX, double nowyY) :
x(nowyX), y(nowyY) { }
double dlugosc() const {
return sqrt(x*x + y*y);
}
double dlugosc() const {
return sqrt(x*x + y*y);
}
wektor2d suma(const wektor2d& b) const
{
wektor2d suma(const wektor2d& b) const
{
wektor2d wynik;
wynik.x = x + b.x;
wynik.y = y + b.y;
return wynik;
}
return wektor2d(x + b.x, y + b.y);
}
wektor2d negacja() const {
void negacja() {
x = -x;
y = -y;
}
void wypisz(std::ostream& out) const {
out << "(" << x << "," << y << ")";
}
return wektor2d(-x, -y);
}
void wypisz(std::ostream& out) const {
out << "(" << x << "," << y << ")";
}
};
};
int main() {
wektor2d a, b, c;
a.x = 4; a.y = 6;
b.x = 1; b.y = 2;
b.negacja();
c = a.suma(b);
std::cout << "Dlugosc wektora ";
c.wypisz(std::cout);
std::cout << " wynosi: "
<< c.dlugosc()
<< std::endl;
int main() {
wektor2d a(4, 6), b(1, 2);
wektor2d c = a.suma(b.negacja());
std::cout << "Dlugosc wektora ";
c.wypisz(std::cout);
std::cout << " wynosi: "
<< c.dlugosc()
<< std::endl;
return 0;
return 0;
}
}
Przeciążanie operatorów
 Definiując w programie klasę, definiujemy nowy typ, dla którego można
określid nowe operacje.
 Przeciążanie operatorów to proces modyfikowania znaczenia
operatorów, np. operatora plus (+), na potrzeby określonej klasy.
 Dzięki przeciążaniu operatorów można uprościd typowe operacje na
obiektach klasy i poprawid czytelnośd programu.
 Słowo kluczowe operator służy do oznaczania przedefiniowania
operatora.
 Przeciążając operator definiujemy funkcję, którą kompilator ma
wywoływad za każdym razem, gdy klasa korzysta z przeciążonego
operatora. Ta funkcja realizuje odpowiednią operację.
 Jeśli program przedefiniowuje operator na potrzeby konkretnej klasy, to
znaczenie tego operatora zostaje zmienione tylko w obrębie tej klasy. W
pozostałych miejscach programu operator będzie wykonywad operacje
zgodnie z jego standardowym znaczeniem.
 Można przedefiniowywad większośd operatorów, jednak są też operatory
których nie można przeciążad.
Zamiast używad nowych nazw funkcji, możemy przeciążyd znane operatory
i w ten sposób uzyskad większą czytelnośd kodu,
np. zamiast funkcji suma, możemy napisad operator+ i dalej w kodzie
stosowad a + b zamiast a.suma(b).
Przykład 8.6.
Operatory przeciążone
class wektor2d {
public:
double x;
double y;
wektor2d() : x(0), y(0) { }
wektor2d(double nowyX, double nowyY) :
x(nowyX), y(nowyY) { }
double dlugosc() const {
return sqrt(x*x + y*y);
}
wektor2d operator+(const wektor2d& b) const {
return wektor2d(x + b.x, y + b.y);
}
wektor2d operator-() const {
return wektor2d(-x, -y);
}
void wypisz(std::ostream& out) const {
out << "(" << x << "," << y << ")";
}
};
int main() {
wektor2d a(4, 6), b(1, 2);
wektor2d c = a + (-b);
std::cout << "Dlugosc wektora ";
c.wypisz(std::cout);
std::cout << " wynosi: "
<< c.dlugosc()
<< std::endl;
return 0;
}
Na ekranie zobaczymy:
Dlugosc wektora (3,4) wynosi: 5
Funkcje zaprzyjaźniowe – gdy parametr operatora nie jest składową
klasy
Zauważmy, że lewa strona operatora+ (tzn. "a") jest obiektem klasy wektor2d.
Gdyby tak nie było, nie moglibyśmy zastosowad tej notacji.
Jeżeli jednak zachodzi koniecznośd operacji nie na zmiennych klasy wektor2d
to stosujemy funkcje zaprzyjaźnione.
Funkcja zaprzyjaźniona (friend) to taka funkcja, która ma dostęp do
prywatnych składowych klasy.
Tworzymy zatem
friend void operator<<(std::ostream& out, wektor2d& wektor) { ... }
Zauważmy, że dla np. dla operator+ możemy pisad
a+b+c
co oznacza
(a + b) + c
Jest to możliwe bo operator+ zwraca obiekt typu wektor2d.
Dla operator<< możemy uzyskac ten sam efekt zwracajac lewy (pierwszy)
argument funkcji operator<<, czyli:
friend std::ostream& operator<<(std::ostream& out, wektor2d& wektor) { ... }
i teraz możemy napisad
std::cout << a << b << std::endl;
Do programu dodajemy zatem funkcję zaprzyjaźnioną:
friend std::ostream& operator << (std::ostream& out,
wektor2d& wektor)
{
out << "(" << wektor.x << "," << wektor.y << ")";
return out;
}
int main() {
wektor2d a(4, 6), b(1, 2);
wektor2d c = a + (-b);
std::cout << "Dlugosc wektora " << c;
std::cout << " wynosi: " << c.dlugosc() << std::endl;
return 0;
}
Przeciążony operator << ’potrafi’ wyświetlid składowe wektora c.
Jakich operatorów nie wolno przeciążać
W tabeli wymieniono operatory, których nie wolno przeciążać.
Operator
Opis
Przykład
.
Wybór składowej klasy obiekt.skladowa
.*
Wskaźnik składowej
obiekt.*skladowa
::
Widoczność
nazwa_klasy::skladowa
?:
Wyrażenie warunkowe c = (a>b) ? a : b;

Podobne dokumenty