Odwieczny dylemat Programisty C++ – zobacz, kiedy użyć jakiego operatora rzutowania.

1. static_cast

Najbardziej przydatny, używaj do:

  • rzutowania kompatybilnych typów i wskaźników, np. double -> int, long -> char, int* -> void*

Przykład:

int main()
{
	const double PI = 3.14159265358979323846264279502;
	int integer_pi = static_cast<int>(PI);

	 . . . 
	return 0;
}

2. dynamic_cast

Używaj do rzutowania wskaźników bazowych na pochodne (w dół hierarchii dziedziczenia), gdy nie jesteś pewien kompatybilności typów (czyli że takie rzutowanie rzeczywiście ma sens). W razie niekompatybilności:

  • zwróci wartość NULL, w przypadku rzutowania wskaźników
  • rzuci wyjątek std::bad_cast, w przypadku rzutowania referencji

Możesz w ten sposób sprawdzić, czy obiekt należy do danej klasy.

Przykład:

class Car {};

class Honda : public Car {};

int main()
{
	Car *car = new Honda();
	Honda *honda = dynamic_cast<Honda*>(car);

	 . . . 
	delete honda;
	return 0;
}

3. reinterpret_cast

Używaj do:

  • rzutowania kompletnie niepowiązanych typów, np. char* -> long. Zwiększa to podatność aplikacji na błędy, bo kompilator nie ostrzeże Cię, gdy zrobisz nawet całkowicie bezsensowne rzutowanie typu książka -> jabłko. Używaj ostrożnie.

Przykład:

int main()
{
	const char *str = "lalala";
	long int str_addr = reinterpret_cast<long int>(str);
	cout << "Napis " << str << " znajduje sie pod adresem " << str_addr;

	return 0;
}

4. const_cast

Używaj do:

  • rzutowania stałych na zmienne i zmiennych na stałe: const T -> T lub T -> const T
  • rzutowania volatile T -> T lub T -> volatile T

Przydatne, gdy jakas funkcja oczekuje inaczej zmodyfikowanego parametru, niż posiadamy, np. chce char*, a my mamy const char*. Często sygnalizuje on źle zaprojektowany fragment kodu. Jeśli musisz go użyć – prawdopodobnnie  coś w kodzie nie zostało do końca przemyślane ;)

Przykład:

void write(char *s)
{
	cout << s;
}

int main()
{
	const char *str = "lalala";
	write(const_cast<char*>(str));

	return 0;
}

PS. Tak naprawdę nie można zrobić zmiennej ze stałej. Operator const_cast sprawia, że kompilator “przymyka oko”, pozwalając przesłać stałą do funkcji, która oczekuje zmiennej. Ta funkcja będzie mogła tylko czytać ten parametr! Gdy spróbuje zapisać – kompilator zaprotestuje.

5. (T)obj, T(obj)

Najsilniejsza konstrukcja. Nie używaj jej, bo:

  • automatycznie dobiera taki typ rzutowania, na który pozwoli kompilator. Dlatego jest niebezpieczna – ostatecznie, mimo woli, możesz wylądować w reinterpret_cast, i rzutować typy całkiem niekompatybilne, bez słowa skargi od kompilatora. Próbuje dopasować  rodzaj rzutowania w kolejności:

const_cast -> static(const)_cast -> reinterpret(const)_cast

Dobra rada

Kiedy musisz rzutować typy – stosuj rzutowanie najbardziej restrykcyjne. Wtedy kompilator Cię chroni przed popełnieniem błędu. Próbuj w kolejności:

const_cast -> static_cast -> dynamic_cast -> reinterpret_cast

I na koniec – nie używaj konstrukcji (T)obj ani T(obj), a nie wpadniesz w kłopoty ;)

Related Posts: