Języki programowania III
C++
Konwersje typów
Wykład dla 2-go roku Informatyki Stosowanej
Wydział Fizyki i Informatyki Stosowanej UŁ
W C++ w stosunku do C została zaostrzona kontrola typów.
Główna zmiana dotyczy wskaźników na typ void*.
W C były one zupełnie bezkarne i można było przydzielać wskaźniki void* do każdych innych.
W C++ są na równi z innymi typami.
Teoretycznie kod napisany w C
Kod w C, bez problemu skompilowany w kompilatorze tegoż języka:
int* wskaznik = malloc(sizeof(int));
nie zostanie skompilowany w kompilatorze C++, z powodu zaostrzonej kontroli typów, dlatego w C++ należey posłużyć się konwersją do docelowego typu wskaźnikowego:
int zmienna_calkowita = int(zmienna_rzeczywista);
W C++ nadal można używać takiego rzutowania,jest ono nazywane "rzutowaniem w stylu C".
Oprócz tego C++ oferuje
"rzutowanie w stylu funkcyjnym":
W języku C rzutowanie wyglądało w następujący sposób:
int zmienna_calkowita = (int)zmienna_rzeczywista;
C++ wprowadza inny sposób zapisu.
Dodatkowo rzutowanie podzielono na cztery typy:
static_cast
const_cast
reinterpret_cast
dynamic_cast
C++ wprowadza inny sposób zapisu.
Dodatkowo rzutowanie podzielono na cztery typy: 1. Proste rzutowanie
static_cast
const_cast
2. Rzutowanie ze zmiennych z modyfikatoremconst i volatile na zmienne bez tych modyfikatorów
static_cast – najczęściej używane.
Gdy konwersja "implicit" traci informację lub kompilator produkuje "worningi". Usuwanie niejednoznaczności.
const_cast – dla zastosowania lub usunięcia
C++ wprowadza inny sposób zapisu.
Dodatkowo rzutowanie podzielono na cztery typy:
reinterpret_cast
3. Niebezpieczne rzutowania, które zmieniają zupełnie sens interpretacji bitów w zmiennych
dynamic_cast
4. Rzutowanie wskaźników na obiekty
dynamic_cast – używając polimorficznych wskaźników lub referencji
dla zmiany (do góry lub do dołu) hierarchii klas. Gdy dynamic_cast wykonuje sprawdzanie,
czy typ obiektu jest zgodny z oczekiwanym, jeśli nie – zwracany NULL
reinterpret_cast – jest używane przy konwersji typów niepowiązanych,
np. niepowiązane wskaźnikim i referencje
Sposób użycia nowych operatorów rzutowania:
int zmienna_całkowita = static_cast<int>(zmienna_rzeczywista);
Static_cast :
• do konwersji podstawowych typów liczbowych, np. int na float.
• do konwersji zdefiniowanych przez użytkownika.
• do konwersji wskaźnika na obiekt klasy pochodnej na wskaźnik na obiekt klasy podstawowej (tak zwane rzutowanie do góry hierarchii dziedziczenia).
• do konwersji wskaźnika na obiekt klasy podstawowejna wskaźnik na obiekt klasy pochodnej (tak zwane rzutowanie w dół hierarchii).
Do czego static_cast nie służy:
* Do rzutowania wskaźników na różne typy,
jeśli nie ma specjalnie zdefiniowanej konwersji między tymi wskaźnikami.
Przykładowo nie skompiluje się static_cast<int*>(i), jeśli zmienna i jest typu unsigned int*
Nie uda się też rzutowanie ze wskaźnika na typ stały
(z modyfikatorem const) na wskaźnik na typ niestały.
* Do dynamicznego sprawdzania, czy rzutowanie mogłoby się powieść.
Nie miałoby to sensu,
Poprawne
użycie static_cast:
#include <iostream>
int main() {
int liczba = 5, liczba2 = 2;
std::cout << "5 / 2 int(bez rzutowania): " << liczba / liczba2 << std::endl; std::cout << "5/2 float(static_cast): " <<
static_cast<float>(liczba) / static_cast<float>(liczba2)<< std::endl; return 0;
Niepoprawne
użycie static_cast:
#include <iostream> int main() {
std::string str = "ciąg";
std::cout << "string --> char: " << static_cast<char>(str) << std::endl; return 0;
Inne Cechy static_cast:
• wyrażenia, które nie dokonują żadnej konwersji mogą być również opisane operatorem static_cast,
np. int i=static_cast<int>(8);
można to bezpiecznie usunąć z kodu nie korzystającego z szablonów.
• Wszędzie, gdzie mówi się o wskaźnikach, można mówić oreferencjach. Obowiązują je te same reguły.
• Działanie rzutowania static_cast zależy od informacji o typach dostępnych czasie kompilacji.
Stąd słowo "static" w "static_cast".
Kompilator nie dodaje "z własnej inicjatywy" kodu binarnego,
więc static_cast można używać również w tzw. wąskich gardłach programu. (jakiś kod oczywiście jest dodawany przez kompilator)
const_cast
Syntax:T const_cast<T> (object);
dla "usunięcia"const lub volatile z obiektu.
Typ targetu musi być tego samego typu co źródło.
Typ T musi być typem wskaźnikowym lub referowanym.
Np. użycie const_cast usuwa kwalifikator const z obiektu: class Foo {
public:
void func() {}
// a non-const member function
};void someFunction( const Foo& f ) {
f.func();
// compile error: cannot call a non-const
// function on a const reference
Foo &fRef = const_cast<Foo&>(f);
dynamic_cast
Syntax:
T& dynamic_cast<T&> (object); T* dynamic_cast<T*> (object);
Dla konwersji obiektu ze wskaźnika lub referencji jednego typu do innego typu.
Przy próbie konwersji do wskaźnika typu, który nie jest typem aktualnym – wynikiem rzutowania będzie NULL.
dynamic_cast
struct A { virtual void f() { } }; struct B : public A { }; struct C { }; void f () { A a; B b; A* ap = &b;B* b1 = dynamic_cast<B*> (&a); // NULL, ponieważ 'a' nie jest typu 'B' B* b2 = dynamic_cast<B*> (ap); // 'b'
C* c = dynamic_cast<C*> (ap); // NULL.
A& ar = dynamic_cast<A&> (*ap); // Ok. B& br = dynamic_cast<B&> (*ap); // Ok.
C& cr = dynamic_cast<C&> (*ap); // std::bad_cast }
Funkcja Konwersji
Np. class Y { int b; public: operator int(); }; Y::operator int() { return b; }void f(Y obj) {
int i = int(obj); int j = (int)obj; int k = i + obj; }
Wszystkie trzy konwersje w funkcji f(Y) użyją funkcji konwertującej Y::operator int().
Classes, enumerations, typedef names, function types, or array types cannot be declared or defined in the conversion_type.
Można użyć funkcji konwertującej dla konwersji obiektu typu A do typu A,
do typu bazowego klasy A lub do void.
Funkcja konwertująca nie ma argumentów i zwraca typ będący typem konwertowanym. Funkcja konwertująca nie może być dziedziczona.