Konwersje
Idea konwersji
" dla typów wbudowanych dopasowanie argumentów
funkcji nie musi być dokładne w razie potrzeby niejawna
zamiana typu (konwersja)
#include
void kwadrat(int k)
{
cout << "zmienna = " << k << " a jej kwadrat = " << k*k <}
main()
{
float a=3.5;
kwadrat(a);
}
" jak to wygląda dla klas przykład klasy zespol (dla liczb
zespolonych)
class zespol {
float re;
float im;
public:
zespol(float r, float i) : re(r), im(i) { }
friend zespol dodaj(zespol, zespol);
};
zespol dodaj(zespol z1, zespol z2)
{
zespol suma;
suma.re=z1.re+z2.re;
suma.im=z1.im+z2.im;
return suma;
}
zespol a(2, 1), b(3,-2), s(0,0);
s=dodaj(a,b); // wynik (5, -1)
" liczba rzeczywista szczególny przypadek zespolonej
czy dopuszczalne poni\sze wywołania ?
zespol a(2, -1), s(0, 0), c(0, 0)
float x=3.0;
s=dodaj(a, x);
c=dodaj(a, 3.5
odpowiedz nie, bo niezgodność typu argumentu
" mo\liwe rozwiązanie przeładowanie funkcji dodaj
o druga wersja funkcji dodaj:
zespol dodaj(zespol z1, float g)
{
zespol suma;
suma.re=z1.re+g;
suma.im=z1.im;
return suma;
}
o dodanie deklaracji przyjazni w klasie zespol dla
drugiej wersji funkcji:
friend zespol dodaj(zespol, float);
o wtedy wywołanie
c=dodaj(a, 3.5); // O.K.
c=dodaj(3.5, a); // nie
o aby ostatnie wywołanie było O.K. konieczna trzecia
wersja funkcji dodaj
" inne podejście powiedzieć kompilatorowi, \e float to
szczególny rodzaj zespol, tak by w razie potrzeby mógł on
dokonać zamiany to pozwoliłoby u\ywać tylko jednej
wersji funkcji dodaj
" ogólnie: sposób konwersji obiektów typu A w obiekty typu
B mo\na zadać na dwa sposoby:
o jako konstruktor klasy B zale\ny od obiektu typu A
o jako specjalna metoda klasy A tzw. funkcja
konwertująca (konwertor)
" główny punkt bez względu na sposób implementacji
operacja konwersji mogą być wykonywane niejawnie (bez
udziału programisty) gdy
o jest niedopasowanie typu
o jest zdefiniowany sposób konwersji
operacja konwersji zostanie wykonana
Konwersja przez konstruktor
" nale\y zdefiniować konstruktor w ciele klasy docelowej
który mo\e być wywołany z tylko jednym argumentem
(obiektem typu wyjściowego)
Przykład: float zespol
zespol::zespol(float r) : re(r), im(0) { }
lub u\ycie standardowego konstruktora z obu argumentami
domyślnymi:
class zespol {
float re;
float im;
public:
zespol(float r=0, float i=0) : re(r), im(i) { }
friend zespol dodaj(zespol, zespol);
};
zespol dodaj(zespol z1, zespol z2)
{
zespol suma;
suma.re=z1.re+z2.re;
suma.im=z1.im+z2.im;
return suma;
}
zespol a(2, 1), b(3,-2), s;
s=dodaj(a,b);
s=dodaj(a, 3.5); // dodaj(a, zespol(3.5))
s=dodaj(5, a); // dodaj(zespol(5),a)
s=dodaj(3.2, 2.5); // dodaj(zespol(3.2),zespol(2.5))
" tak mo\na konwertować nie tylko obiekty typów
wbudowanych (float, int), ale obiekty klas
Przykład: klasa osoba
class osoba {
char imie[20];
char nazwisko[30];
int waga,
int wzrost
public:
osoba(char *i, char *n, int wi, int wg) : wiek(wi), waga(wg)
{
strcpy(imie,i);
strcpy(nazwisko,n);
}
Aby mieć konwersję osoba zespol trzeba
zdefiniować konstruktor w klasie zespol zale\ny od
argumentu typu osoba (tu decydujemy jak składowe klasy
osoba określają składowe klasy zespol)
zadbać, by ten konstruktor miał dostęp do potrzebnych
składowych klasy osoba mo\na to zrobić na 3 sposoby
o uczynić potrzebne składowe publicznymi
o poprzez deklarację przyjazni w klasie wyjściowej
o poprzez wyposa\enie klasy wyjściowej w zestaw
publicznych metod
Przykład:
#include
class osoba;
class zespol {
float re;
float im;
public:
zespol(float r=0.0, float i=0.0) : re(r), im(i) {}
zespol(osoba o);
void wypisz(){cout <<"("< friend zespol dodaj(zespol,zespol);
};
class osoba {
char imie[20];
char nazwisko[30];
int wiek;
int waga;
public:
osoba(char *i, char *n, int wi, int wg);
friend zespol::zespol(osoba o);
};
zespol::zespol(osoba o)
{
re=o.wiek;
im=o.waga;
}
osoba::osoba(char *i, char *n, int wi, int wg) : wiek(wi), waga(wg)
{
strcpy(imie,i);
strcpy(nazwisko,n);
}
zespol dodaj(zespol z1, zespol z2)
{
zespol suma;
suma.re=z1.re+z2.re;
suma.im=z1.im+z2.im;
return suma;
}
main()
{
zespol a(1,2), b(2,-3),c;
c=dodaj(a,b);
cout << "a + b = ";
c.wypisz(); cout << endl;
c=dodaj(a,1.5);
cout << "a + 1.5 = ";
c.wypisz(); cout << endl;
c=dodaj(-2,a);
cout << "-2 + a = ";
c.wypisz(); cout << endl;
c=dodaj(-2,3);
cout << "-2 + 3 = ";
c.wypisz(); cout << endl;
osoba o("Jan","Nowak",20,72);
c=dodaj(a,o);
cout << "a + o = ";
c.wypisz(); cout << endl;
c=dodaj(o,10);
cout << "o + 10 = ";
c.wypisz(); cout << endl;
}
a + b = (3, -1)
a + 1.5 = (2.5, 2)
-2 + a = (-1, 2)
-2 + 3 = (1, 0)
a + o = (21, 74)
o + 10 = (30, 72)
Konwersja przez funkcję konwertującą
" czy ka\dą konwersję mo\na zrealizować przez
konstruktor?
nie gdy typ docelowy wbudowany, to nie mo\emy tam
zdefiniować konstruktora (np. konwersja zespol float)
podobnie gdy nie mamy dostępu do kodu klasy
docelowej (np. jest to klasa biblioteczna)
" wyjście definicja specjalnej funkcji składowej klasy
wyjściowej (funkcji konwertującej, konwertora)
" je\eli chcemy skonwertować obiekt klasy K do typu T, to
konwertor
o nazywa się: operator T
o nie ma \adnych argumentów
o nie ma określenia typu zwracanego
czyli jest funkcją
K::operator T()
Przykład: definiujemy konwersję zespol int określoną jako część
całkowitą z sumy składowej rzeczywistej i urojonej
class zespol {
// ..
operator int()
{
return (int) re+im;
}
};
" u\ycie wszędzie gdzie spodziewany jest int mo\emy
umieścić zespol zostanie dokonana automatyczna
konwersja
Przykład:
void szlaczek(char znak, int ile)
{
for(int k=0; kcout << znak;
}
zespol z(2.3, 3.1);
szlaczek( * , 5);
szlaczek( % , z);
" mo\liwe jawne wywołanie konwersji
int k;
zespol a(2, 3);
k=(int) a; // rzutowanie
k=int(a); // wywołanie funkcji
" ogólne uwagi o funkcji konwersji:
o konwertor realizowany jako metoda klasy wyjściowej
ma dostęp do wszystkich składowych klasy
wejściowej (przez wskaznik this)
o nazwa określa typ na który konwertujemy (dlatego
określenie typu wyniku zbędne)
o konwerter nie ma \adnych argumentów
przeładowania są niemo\liwe
o konwertor podlega dziedziczeniu
o konwertor mo\e być funkcją wirtualną
" konwerter mo\e równie\ dokonywać konwersji do typu
zdefiniowanego
Przykład 1: konwesja zespol osoba:
imie = Z , nazwisko= Z , wiek=(int) |re|, waga=(int)|im|
class zespol {
// ..
operator osoba()
{
osoba tmp( Z , Z , (int) fabs(re), (int)fabs(im));
return tmp;
}
};
Przykład 2: definicja i u\ycie konwersji
#include
class osoba;
class zespol {
float re;
float im;
public:
zespol(float r=0.0, float i=0.0) : re(r), im(i) {}
zespol(osoba o);
operator float() {return re+im;}
operator osoba();
friend zespol dodaj(zespol, zespol);
};
class osoba {
char imie[20];
char nazwisko[30];
int wiek;
int waga;
public:
osoba(char *i, char *n, int wi, int wg);
osoba(float k);
operator float(){return wiek;}
friend zespol::zespol(osoba o);
friend void dane(osoba);
};
zespol::zespol(osoba o)
{
re=o.wiek;
im=o.waga;
}
zespol::operator osoba()
{
osoba o("Z","Z",(int)fabs(re),(int)fabs(im));
return o;
}
osoba::osoba(char *i, char *n, int wi, int wg) : wiek(wi), waga(wg)
{
strcpy(imie,i);
strcpy(nazwisko,n);
}
osoba::osoba(float k) : wiek(fabs(k)), waga(fabs(k))
{
strcpy(imie,"F");
strcpy(nazwisko,"F");
}
zespol dodaj(zespol z1, zespol z2)
{
zespol suma;
suma.re=z1.re+z2.re;
suma.im=z1.im+z2.im;
return suma;
}
void dane(osoba o)
{
cout << o.imie << " " << o.nazwisko << ", wiek: ";
cout << o.wiek << ", waga: " << o.waga << endl;
}
void kwadrat(float a)
{
cout << "Kwadrat: " << a*a << endl;
}
main()
{
float a=3.14;
osoba o("Jan","Nowak",20,72);
zespol z(20,-72);
kwadrat(a);
kwadrat(o);
kwadrat(z);
cout << "=================================\n";
dane(o);
dane(a);
dane(z);
system("PAUSE");
}
Kwadrat: 9.8596
Kwadrat: 400
Kwadrat: 2704
=================================
Jan Nowak, wiek: 20, waga: 72
F F, wiek: 3, waga: 3
Z Z, wiek: 20, waga: 72
Konwersja wybór wariantu
" w ogólności konwersja A B mo\e być realizowana jako
o konstruktor w klasie B zale\ny od argumentu typu A:
B::B(A)
o funkcja konwertująca w klasie A:
o A::operator B()
" ogranicznia sposób realizacji:
o gdy A typ wbudowany konwersja tylko przez
konstruktor w B
o gdy B typ wbudowany konwersja tylko przez
funkcję konwertującą w A
" gdy oba typy zdefiniowane wybieramy jedną z realizacji
(inaczej niejednoznaczność przy niejawnym wywołaniu)
" który wariant jest lepszy?
" wady realizacji konwersji przez konstruktor:
o gdy klasa B nie jest naszą własnością (np. klasa
biblioteczna) nie mo\emy modyfikować jej kodu
nie mo\emy napisać konstruktora
o by napisać konstruktor w B zale\ny od obiektu A
musimy zagwarantować dostęp do potrzebnych
składowych typu A poprzez
uczynienie ich prywatnymi,
deklarację przyjazni do konstruktora w B
Wniosek: musimy modyfikować obydwie klasy!!!
o argumenty konstruktora konwertującego muszą
dokładnie pasować nie są mo\liwe konwersje
standardowe
o konstruktorów się nie dziedziczy (w przeciwieństwie
do innych metod klasy) konwersja zrealizowana
przez funkcję konwertującą jest dziedziczona zaś
przez konstruktor nie
" wnioski wskazówki ogólne ma temat realizacji konwersji:
o aby zrealizować konwersję A B powinniśmy u\yć
funkcji konwersji w klasie A ten sposób nie
powoduje konieczności ingerencji w klasę B
o gdy A niedostępna konwersja przez konstruktor w
klasie B; będzie to mo\liwe, gdy klasa A zapewnia
wystarczający dostęp do potrzebnych składowych
Konwersje niejawne wywołania
Główna cecha konwersji automatyzm ich wywołania.
Sytuacje w których to ma miejsce to:
" niedopasowanie argumentów funkcji (niezgodność typów
argumentów formalnych i aktualnych) + jednoznacznie
określony sposób konwersji)
zespol z(1,2);
float kwadrat(float a) { return a*a;}
float b;
b=kwadrat(z); // konwersja do float przed wywołaniem funkcji
" niezgodność typu obiektu zwracanego przez return z
deklarowanym typem funkcji
float obetnij(zespol z)
{
return z; // konwersja zwracanej wartości
}
" w obecności operatorów by doprowadzić do sytuacji, gdy
oba argumenty są tego samego typu
zespol z(1,3);
3.5 + z; // konwersja z na double
" w wyra\eniach, których spodziewamy się określonego typu
zespol z;
if(z) // konwersja z do int
switch(z)
while(z)
" w wyra\eniach inicjalizacyjnych
zespol z(1,-2);
float x=z; // konwersja z do float
Jawne wywołania konwersji
" mo\liwe jawne wywołania konwersji
zespol z(15, 50);
osoba n;
n=osoba(z); // forma wywołania funkcji
n=(osoba) z; // forma rzutowanie
" dopuszczalne dwie formy (wywołanie funkcji, rzutowanie),
na ogół równowa\ne
" okoliczności, w których te formy nie są równowa\ne
o przewaga wywołania funkcji konwersja przez
konstruktor, który mo\e mieć więcej argumentów
o przewaga rzutowanie konwersja na skomplikowany
typ
zespol z;
char *napis;
napis=(char*)z; // O.K.
Konwersje przy niedopasowaniu
" do tej pory konwersja przy dokładnym dopasowaniu
typów (jest typ A, potrzeba B, jest sposób konwersji A B)
" konwersja mo\liwa równie\ przy niepełnym dopasowaniu;
polega ona wtedy na (niejawnym) zło\eniu konwersji
zdefiniowanej przez u\ytkownika i konwersji standardowej
lub konwersji jawnej i konwersji niejawnej
" zasady tworzenia takich zło\eń
o największy priorytet dokładne dopasowanie (bez
konwersji)
o w zło\eniach konwersja zdefiniowana mo\e się
(niejawnie) pojawić tylko raz
o konwersje standardowe mają przewagę nad
zdefiniowanymi przez u\ytkownika
o wybór konwersji musi być jednoznaczny
o liczba zło\eń powinna być jak najmniejsza
Przykłady:
" mamy przeładowane funkcje
f(int); // wersja 1
f(zespol) // wersja 2
oraz konwersję zespol int
która wersja funkcji zostanie wywołana przez instrukcje:
zespol z(1, 2);
f(z);
" mamy funkcję f(float) oraz konwersję zespol int
czy poni\sze instrukcje są legalne?
zespol z(1, 2);
f(z);
" mamy funkcję f(Z) oraz konwersje: X Y oraz Y Z
czy poni\sze instrukcje są legalne?
X x;
f(x);
Uwaga: wywołanie f((Y)x) legalne
" mamy przeładowane funkcje:
f(float); // wersja 1
f(zespol); // wersja 2
oraz konwersję int zespol
która wersja zostanie u\yta w wywołaniu:
f(100);
" mamy przeładowane funkcje:
f(float); // wersja 1
f(int); // wersja 2
oraz konwersje: zespol float; zespol int;
która wersja funkcji zostanie u\yta w wywołaniu:
zespol z(1,2);
f(z);
Konwersje uwagi ogólne
" złe u\ycie konwersji mo\e zagmatwać sytuację
" wskazówki na temat u\ycia
o poprawny schemat u\ycia
A
B D
C
(dorzecze)
o niepoprawny schemat u\ycia
L
K M
N
(eksplozja)
" ka\da klasa powinna mieć jeden operator konwersji
" konwersja powinna iść od obiektu bardziej zło\onego do
obiektu o prostszej strukturze
" nie ka\da konwersja ma sens (np. zespol osoba)
Wyszukiwarka
Podobne podstrony:
Konwerter USB na podczerwień
Schemat konwertera radio FM satelity NOAA
Konwersja Filmów Z Płyt Dvd Do Formatu Divx
Super kalkulator konwerter CONVERT
konwerter i wzmacniacz optyczne i coaksiale coaxial
Konwersatorium z Fizyki Zestaw 5 Chemia II
KONWERTER z 88 108Mhz na 64 74Mhz opis
349 Konwersja pożyczki na udziały ujęcie w księgach rachunkowych
Konwersja z DV na DVD Poradnik
Konwersacje na wakacje czesc 2
cs w16
więcej podobnych podstron