1 Kurs języka C++ – wykład 9 (28.04.2014)Konwersje
2 Spis treści Tradycyjne operatory rzutowania Konstruktory konwertująceOperatory konwersji Rzutowanie static_cast Rzutowanie const_cast Rzutowanie reinterpret_cast Rzutowanie dynamic_cast RTTI – operator typeid() Automatyczne określanie typu (auto) Wydobycie typu wyrażenia (decltype)
3 Tradycyjne operatory rzutowaniaTradycyjne operatory rzutowania jawnie przekształcają typ danych. Tradycyjne operatory konwersji mogą przyjmować dwie formy: (typ)wyrażenie typ(wyrażenie) Przykłady: (int) // forma rzutowania double(7*11+5) // forma konstruktorowa Operacja jawnej konwersji typów jest niebezpieczna i należy ją stosować bardzo ostrożnie (tylko w razie konieczności). Zaleca się używać konstruktorowej formy przy rzutowaniu tradycyjnym.
4 Tradycyjne operatory rzutowaniaKompilator umie przekształcać na siebie wszystkie typy podstawowe. Operator rzutowania eliminuje ostrzeżenia kompilatora przy przekształcaniu typów podstawowych. Kompilator nie będzie generował ostrzeżeń w przypadku konwersji na typach podstawowych, w których mamy do czynienia z promocją (konwersje niejawne). Przykłady: const double e = ; int x = (int)e; // wymagana konwersja double y = 2*x+1; // konwersja niejawna
5 Konstruktory konwertująceKonstruktor konwertujący to konstruktor bez deklaratora explicit, który można wywołać z jednym parametrem: K::K (typ x) {/*…*/} // typ!=K Konstruktorów konwertujących może być wiele w jednej klasie. Deklarator explicit zabrania używać konstruktora konwertującego niejawnie.
6 Konstruktory konwertującePrzykład konstruktora konwertującego i jego niejawnego użycia: class zespolona { double re, im; public: zespolona (double r=0, double i=0); // … }; // … zespolona a; zespolona b = zespolona(1.2); // jawna konwersja zespolona c = 3.4; // niejawna konwersja zespolona d = (zespolona)5.6; // rzutowanie zespolona e = static_cast
7 Operatory konwersji Operator konwersji ma następującą postać: operator typ (); Operator konwersji ma pustą listę argumentów i nie ma określonego typu wyniku (typ wyniku jest określony poprzez nazwę tego operatora). Operator konwersji musi być funkcją składową w klasie. Operator konwersji jest dziedziczony. Operator konwersji może być wirtualny. Operatorów konwersji może być wiele w jednej klasie.
8 Operator static_cast Operator rzutowania static_cast ma następującą postać: static_cast
9 Rzutowanie const_castOperator rzutowania const_cast ma następującą postać: const_cast
10 Rzutowanie reinterpret_castOperator rzutowania reinterpret_cast ma następującą postać: reinterpret_cast
11 Rzutowanie dynamic_castOperator rzutowania dynamic_cast ma następującą postać: dynamic_cast
12 RTTI Operator typeid() zwraca referencję do obiektu opisującego typ wyrażenia w nawiasach (można też podać nazwę typu). Klasa type_info zdefiniowana w
13 Automatyczne określanie typuW definicji zmiennej z jawnym inicjowaniem można użyć słowa kluczowego auto – można w ten sposób utworzyć zmienną o typie takim, jak typ inicjującego wyrażenia. Przykład 1: auto jakasZmienna = L"To jest tekst"; Typ jakasZmienna jest programiście łatwiej napisać słowo auto niż const wchar_t * (taki jak dla literału tekstowego). Przykład 2: auto innaZmienna = boost::bind(&Funkcja, _2, _1, Obiekt); Typem innaZmienna może być cokolwiek zwracanego przez pewną funkcję szablonową pod boost::bind dla danych argumentów, typ ten jest łatwy do określenia przez kompilator, natomiast dla użytkownika jest to trudne.
14 Automatyczne określanie typuPrztkład 3: Typ auto jest przydatny przy ograniczaniu rozwlekłości kodu. Zamiast pisać: for (vector
15 Wydobycie typu wyrażeniaOperator decltype pozwala na uzyskanie typu wyrażenia. Jego głównym przeznaczeniem tego operatora jest programowanie uogólnione, w którym często trudno, jeśli w ogóle jest to możliwe, określić typy zależne od parametrów szablonu. Typ określony za pomocą operatora decltype zgadza się z typem obiektu lub funkcji zadeklarowanym w kodzie źródłowym. Podobnie jak w przypadku operatora sizeof, operand decltype nie jest wykonywany.
16 Wydobycie typu wyrażeniaPrzykłady: const int& foo(); int i; struct A { double x; }; const A *a = new A(); decltype(i) x2; // typ to int decltype(foo()) x1 = i; // typ to const int& decltype(a->x) x3; // typ to double decltype((a->x)) x4; // typ to const double& Wyrażenie w nawiasie (a->x) nie jest ani id-wyrażenie ani dostępem do członków klasy, a stąd nie oznacza nazwanego obiektu. Ponieważ to wyrażenie jest l-wartością, jego wydedukowany typ jest referencją do typu wyrażenia, czyli const double&.