w16 konwersje


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