Zagadnienia wybrane:agregaty,
tablice obiektów, operatory
przeciążone
Wykład 5
Klasa, która nie posiada metod, czyli funkcji składowych
działających na rzecz obiektów klasy, ani jawnego
konstruktora i której wszystkie składniki są publiczne bywa
nazywana agregatem. Jest to zbiór zmiennych różnych typów
tworzących zestaw danych. Jego elementy mogą być także
obiektami.
Utwórzmy obiekt zawierający imię i wiek osoby i wydrukujmy
zgromadzone dane na ekran.
Przykład:
1. #include <iostream.h>
2. #include <conio.h>
3. #include <string.h>
4. class Osoba
//
definicja klasy
5. {public:
6. char imie[80];
7. int wiek;
8. Osoba(char*,int);
//
deklaracja
konstruktora,
który
oczekuje parametrów formalnych typu tablica znakowa
wskazywana wskaźnikiem i zmienna integer (patrz w.23)
9. };
10.class Osoby
//
definicja
klasy Osoby nie ma jawnego konstruktora, jest
agregatem
.
11.{public:
12.char imie[80];
13.int wiek;
14.};
15.void main()
16.{clrscr();
17.Osoba oa (“Ala”,20);
//
tworzenie obiektu oa klasy Osoba
18.Osoby oy={“Ola”,28};
//
inicjalizacja obiektu-agregatu klasy
Osoby
19.cout<<``oa=,oa.imie<``,``<<oa.wiek``;
20.cout<<``\noy=,oy.imie<``,``<<oy.wiek``;
21.getch();
22.}
23.Osoba::Osoba(char*s,int w):wiek(w) //
inicjalizacja parametru w i
nadanie zmiennym nazw s oraz w
24.{strcpy(imie,s);
//
kopiowanie łańcucha zmiennej s do tablicy
imie w klasie Osoba
25.}
Po wykonaniu mamy na ekranie:
oa=Ala,20
oy=Ola,28
Tablice obiektów definiowanych
Obiekty możemy umieszczać w tablicach tak samo jak typy
wbudowane.
Przykład:
1. #include <iostream.h>
2. #include <conio.h>
3. #include <string.h>
4. class Osoby
//
definicja klasy-agregatu
5. {public:
6. static int n;
//
klasa posiada składnik statyczny tj. inicjowany
trwale przy każdym wywołaniu
7. char imie[80];
8. int wiek;
9. };
10. int Osoby::n=0; //
inicjalizacja statycznej zmiennej n o zasięgu
globalnym
void main()
11.{clrscr();
12.Osoby oy[]={``Ala``,``20``,``Ola``,``28``};
//
inicjalizacja tablicy obiektów tworzacych agregat
13.Osoby::n=3; //
aktualizacja składnika statycznego
14.cout<<”\n
Osoby::n=”<<oy->n;
//
wydruk
wartości n z dostepem poprzez obiekt klasy Osoba tj.
przez wskaznik do tablicy oy
15.for(int i=0;i<Osoby::n;i++)//
popatrz na sposób
wykorzystania statycznej zmiennej jako ogranicznika
powtorzen petli
16.cout<<”\noy[’’<<i<<”]=”<<oy[i].imie<<”,’’<<(oy+i
)->wiek; //
wydruk elementów tablicy oy z odwołaniem
do elementu raz operatorem kropki i raz strzałka
wskaznika. Wskaznik powiekszamy o i w kazdej petli.
17.getch();
18.}
Przeładowanie (przeciążenie) operatorów
Jeżeli na liście argumentów formalnych funkcji mamy co najmniej
dwa argumenty, a w tym jeden utworzony przez programistę, to
funkcję możemy wywołać do działania nie tylko poprzez jej nazwę.
Możemy utworzyć operator wstawiany pomiędzy argumenty
aktualne funkcji, który wywoła ją do działania.
Ten operator to w rzeczywistości definiowanie pewnej własnej
funkcji o postaci:
typ_zwracany operator YYY (argumenty)
{
//
ciało funkcji//
}
YYY jest symbolem operatora, a jednym z argumentów musi być
obiekt definiowanej klasy tj. zmienna własna.
Nie możemy przeładować operatorów o specjalnym zastosowaniu w
technice obiektowej. Są to:
.
. * :: :?
sizeof
Typ zwracany to typ zmiennej, którą powinna zwracać nasza funkcja
– operator.
Przeładowanie (przeciążenie) operatorów
• operator << przeciążony w klasie ostream
umożliwia wysyłanie danych do standardowego
urządzenia wyjściowego, którym jest najczęściej
monitor ekranowy.
• Wartością wyrażenia zawierającego operator << jest
referencja
obiektu
reprezentujący
strumień
wyjściowy, w powyższym przykładzie jest to
wyjscie.
• Operator << został przeciążony dla wszystkich
podstawowych typów danych: char, short, int,
long, float, double, long double, char* i
void.
• Definicję takiego przeciążania zadaje się za pomocą
funkcji zaprzyjaźnionej.
Przeładowanie (przeciążenie) operatorów
• operator >> przeciążony w klasie istream
umożliwia pobieranie danych z wejścia
standardowego. Zastosowanie tego operatora
do obiektu cin (reprezentującego wejście
standardowe) powoduje pobranie z wejścia
odpowiedniej
liczby
znaków,
wykonanie
konwersji i przypisanie zmiennej obliczonej
wartości. Operator >> został przeciążony dla
wszystkich podstawowych typów danych:
char, short, int, long, float, double,
long double oraz char*.
Przeładowanie (przeciążenie) operatorów
• Przeciążanie operatorów to typowy
Potencjalnie znacznie poprawia czytelność kodu i umożliwia
zdefiniowanie większej części
na
poziomie języka, bez uciekania się do trików.
• Z drugiej strony, ta technika programistyczna może
spowodować powstawanie niejasnych konstrukcji, gdzie
operatory wykonują kompletnie różne czynności w zależności
od ich operandów. Na przykład wyrażenie w C++: a<<1
• normalnie oznacza przesunięcie bitowe liczby a w lewo. Jeśli a
jest strumieniem, oznacza to przesłanie liczby 1 do tego
strumienia. Jak jednak interpretować ten fragment gdy a jest
oknem?
• Z tego powodu zalecane jest, aby przeciążać tylko te operatory,
których znaczenia łatwo się domyślić, gdyż mają swój
odpowiednik w matematyce, fizyce itp.
Przeładowanie (przeciążenie) operatorów
• Aby móc wykorzystywać dany operator na
obiekcie klasy musi on zostać przeciążony.
Istnieją dwa wyjątki od tej reguły. Operator
przypisania może zostać użyty z dowolną
klasą. Wówczas domyślnym działaniem jest
przypisanie składowych klasy (w
przypadku,gdy jako dane składowe występują
wskaźniki jest to działanie z reguły
niepożądane). Operator pobierania adresu &
nie musi być przeciążany - będzie zwracał
adres obiektu w pamięci.
Przeładowanie (przeciążenie) operatorów
• Przeciążanie operatorów nie ma wpływu na
kolejność ich wykonywania oraz nie może
zmieniać ich kojarzenia. Nie możemy również
zmieniać liczby operandów (liczby
argumentów) operatora. Nie można tworzyć
nowych operatorów. Przeciążanie działa
jedynie z obiektami typów zdefiniowanych
przez programistę (co oznacza np., że nie
możemy przeciążyć operatora * dla typu float).
Przeładowanie (przeciążenie) operatorów
• Funkcje przeciążające operatory mogą być
składowymi klasy, jak i nie. Funkcje, które nie
są składowymi dosyć często są deklarowane
jako friend (gdyż mają bezpośredni dostęp do
danych prywatnych i w związku z tym są
bardziej wydajne). Podczas przeciążania (), [],
-> lub operatorów przypisania funkcja
przeciążająca musi być funkcją składową klasy.
Jeśli chodzi o inne operatory to nie ma takiego
wymogu.
Przeładowanie (przeciążenie) operatorów
• Jeśli funkcja operatorowa zostanie zadeklarowana jako
składowa, skrajny lewy (jedyny) parametr tej funkcji musi
być obiektem (lub referencją) klasy w której funkcja
operatorowa jest zdefiniowana. Jeśli skrajny lewy parametr
funkcji operatorowej musi być obiektem innej klasy lub
wbudowanego typu danych wówczas funkcja operatorowa
nie może być zadeklarowana jako funkcja składowa klasy.
Funkcje operatorowe >> i << wymagają jako skrajnego
lewego parametru albo referencji do obiektu typu ostream
(operator <<) albo referencji do obiektu typu istream
(operator >>). Dlatego funkcje operatorowe przeciążające
operatory i << nie mogą być metodami składowymi klasy.
Dla uzyskanie większej wydajności zadeklarujemy je jako
funkcje friend naszej klasy. Poniżej podaję przykładową
klasę punkt w której przeciążono operatory << i >>.