Obsada regularna vs statyczna vs dynamiczna [duplikat]

To pytanie ma już odpowiedź tutaj:

Piszę kod C i c++ od prawie dwudziestu lat, ale jest jeden aspekt tych języków, którego nigdy tak naprawdę nie rozumiałem. Oczywiście używałem zwykłych odlewów tzn.

MyClass *m = (MyClass *)ptr;
/ Align = "left" / miejsce, ale wydaje się, że są jeszcze dwa rodzaje odlewów i nie znam różnicy. Jaka jest różnica między poniższymi liniami kodu?
MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
Author: R. Martinho Fernandes, 2008-08-26

8 answers

Static_cast

static_cast jest używany w przypadkach, w których zasadniczo chcesz odwrócić ukrytą konwersję, z kilkoma ograniczeniami i dodatkami. static_cast nie wykonuje żadnych kontroli runtime. Powinno być używane, jeśli wiesz, że odnosisz się do obiektu określonego typu, a zatem sprawdzenie byłoby niepotrzebne. Przykład:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

W tym przykładzie wiesz, że przekazałeś obiekt MyClass, więc nie ma potrzeby sprawdzania środowiska uruchomieniowego, aby zapewnić to.

Dynamic_cast

dynamic_cast jest przydatne, gdy nie wiesz, jaki jest typ dynamiczny obiektu. Zwraca wskaźnik null, jeśli obiekt, do którego się odnosi, nie zawiera typu rzucanego jako klasa bazowa(gdy rzuca się na referencję, w takim przypadku rzuca się wyjątek bad_cast).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Nie możesz użyć dynamic_cast, jeśli przerzucasz do klasy pochodnej, A Typ argumentu nie jest polimorficzny. Na przykład poniższy kod nie jest poprawny, ponieważ Base nie zawiera Dowolna funkcja wirtualna:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

"Up-cast" (cast do klasy bazowej) jest zawsze ważny zarówno z static_cast jak i dynamic_cast, a także bez żadnego cast, ponieważ "up-cast" jest konwersją dorozumianą.

Regularna Obsada

Te odlewy są również nazywane odlewami w stylu C. C-style cast jest w zasadzie identyczny do wypróbowania szeregu sekwencji C++ odlewów, i biorąc pierwszy C++ odlewów, który działa, bez zastanawiania się {6]}. Nie trzeba dodawać, że jest to o wiele potężniejsze, ponieważ łączy wszystkie z const_cast, static_cast i reinterpret_cast, ale jest również niebezpieczny, ponieważ nie używa dynamic_cast.

Dodatkowo, rzuty w stylu C nie tylko pozwalają ci to zrobić, ale także pozwalają bezpiecznie rzucać do prywatnej klasy bazowej, podczas gdy sekwencja" równoważna " static_cast spowodowałaby błąd w czasie kompilacji.

Niektórzy ludzie wolą odlewy w stylu C ze względu na ich zwięzłość. Używam ich tylko do odlewów numerycznych i używam odpowiednich odlewów C++, gdy zaangażowane są typy zdefiniowane przez użytkownika, ponieważ zapewniają dokładniejsze sprawdzanie.

 1432
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-09 03:23:43

Static cast

Statyczny odlew wykonuje konwersje między kompatybilnymi typami. Jest podobny do obsady w stylu C, ale jest bardziej restrykcyjny. Na przykład, odlew w stylu C pozwala na wskazanie wskaźnika liczby całkowitej na znak.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Ponieważ powoduje to 4-bajtowy wskaźnik wskazujący na 1 bajt przydzielonej pamięci, zapis do tego wskaźnika spowoduje błąd czasu wykonania lub nadpisze jakąś sąsiednią pamięć.

*p = 5; // run-time error: stack corruption

W przeciwieństwie do obsady w stylu C, static cast pozwoli kompilatorowi sprawdzić, czy typy danych pointer i pointee są zgodne, co pozwala programiście złapać to nieprawidłowe przypisanie wskaźnika podczas kompilacji.

int *q = static_cast<int*>(&c); // compile-time error

Reinterpretacja odlewu

Aby wymusić konwersję wskaźnika, w taki sam sposób jak C-style C w tle, zostanie użyty reinterpretujący cast.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Ten cast obsługuje konwersje między pewnymi niepowiązanymi typami, takimi jak z jednego typu wskaźnika do inny niezgodny typ wskaźnika. Po prostu wykona binarną kopię danych bez zmiany podstawowego wzorca bitowego. Należy zauważyć, że wynik takiej operacji niskiego poziomu jest specyficzny dla systemu i dlatego nie jest przenośny. Należy go stosować ostrożnie, jeśli nie można go całkowicie uniknąć.

Dynamic cast

Ten jest używany tylko do konwersji wskaźników i odniesień do obiektów na inne typy wskaźników lub referencji w hierarchii dziedziczenia. Jest to jedyna obsada, która upewnia się, że wskazywany obiekt może zostać przekonwertowany, wykonując sprawdzenie w czasie wykonywania, czy wskaźnik odnosi się do kompletnego obiektu typu docelowego. Aby to sprawdzenie było możliwe, obiekt musi być polimorficzny. Oznacza to, że klasa musi zdefiniować lub dziedziczyć co najmniej jedną funkcję wirtualną. Jest tak, ponieważ kompilator wygeneruje tylko potrzebne informacje typu run-time dla takich obiektów.

Dynamiczne przykłady odlewów

W poniższym przykładzie, MyChild wskaźnik jest konwertowany na wskaźnik MyBase przy użyciu Dynamicznego odlewu. Ta konwersja pochodna do bazy powiodła się, ponieważ obiekt potomny zawiera kompletny obiekt bazowy.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

Następny przykład próbuje przekonwertować wskaźnik MyBase na wskaźnik MyChild. Ponieważ obiekt podstawowy nie zawiera kompletnego obiektu potomnego, konwersja wskaźnika nie powiedzie się. Aby to zaznaczyć, dynamiczny cast zwraca wskaźnik null. Daje to wygodny sposób sprawdzenia, czy konwersja się powiodła podczas biegu.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Jeśli Referencja zostanie przekonwertowana zamiast wskaźnika, dynamiczny cast zakończy się niepowodzeniem, rzucając wyjątek bad_cast. To musi być obsługiwane za pomocą instrukcji try-catch.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Dynamiczny lub statyczny odlew

Zaletą użycia dynamicznego odlewu jest to, że pozwala programistom sprawdzić, czy konwersja powiodła się w czasie wykonywania. Wadą jest to, że istnieje narzut wydajności związany z wykonaniem tej kontroli. Na z tego powodu użycie odlewu statycznego byłoby preferowane w pierwszym przykładzie, ponieważ konwersja pochodna do bazy nigdy nie zawiedzie.

MyBase *base = static_cast<MyBase*>(child); // ok

Jednak w drugim przykładzie konwersja może się udać lub nie. Nie powiedzie się, jeśli obiekt MyBase zawiera instancję MyBase i powiedzie się, jeśli zawiera instancję MyChild. W niektórych sytuacjach może to nie być znane do czasu uruchomienia. W takim przypadku Obsada dynamiczna jest lepszym Wyborem niż Obsada statyczna.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Jeśli konwersja bazowa na pochodną została przeprowadzona przy użyciu odlewu statycznego zamiast odlewu dynamicznego konwersja nie zawiedzie. Zwracałby wskaźnik, który odnosił się do niekompletnego obiektu. Dereferowanie takiego wskaźnika może prowadzić do błędów w czasie pracy.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const cast

Ten jest używany głównie do dodawania lub usuwania modyfikatora const zmiennej.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Chociaż const cast pozwala na zmianę wartości stałej, to i tak jest to nieważne kod, który może spowodować błąd w czasie wykonywania. Może to nastąpić na przykład, jeśli stała znajduje się w sekcji pamięci tylko do odczytu.

*nonConst = 10; // potential run-time error

Const cast jest używany głównie wtedy, gdy istnieje funkcja, która przyjmuje niestały argument wskaźnika, mimo że nie modyfikuje on wskaźnika.

void print(int *p) 
{
   std::cout << *p;
}

Funkcja może być następnie przekazana zmiennej stałej za pomocą konstruowania.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Źródło i więcej wyjaśnień

 123
Author: Breeze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-24 01:55:13

Powinieneś zajrzeć do artykułu Programowanie C++ / typowanie.

Zawiera dobry opis WSZYSTKICH rodzajów odlewów. Poniższe zaczerpnięte z powyższego linku:

Const_cast

Const_cast (wyrażenie) const_cast () służy do dodawania/usuwania const (Ness) (lub volatile-Ness) zmiennej.

Static_cast

Static_cast (wyrażenie) static_cast () służy do rzucania pomiędzy typu integer. 'np.' char - > long, int - > short itp.

Static cast jest również używany do rzucania wskaźników do pokrewnych typów, dla przykładowe odlewanie Pustki * do odpowiedniego typu.

Dynamic_cast

Dynamic cast służy do konwersji wskaźników i odniesień w czasie wykonywania, ogólnie w celu rzucenia wskaźnika lub odniesienia w górę lub w dół łańcuch dziedziczenia (hierarchia dziedziczenia).

Dynamic_cast (wyrażenie)

Typ docelowy musi być wskaźnikiem lub odniesieniem Typ, oraz wyrażenie musi być przypisane do wskaźnika lub odniesienia. Dynamic cast works tylko wtedy, gdy typ obiektu, do którego odnosi się wyrażenie jest kompatybilny z typem docelowym, a klasa bazowa ma co najmniej jeden funkcja Wirtualnego członka. Jeśli nie, i rodzaj wyrażeń, które są rzucane jest wskaźnikiem, zwracane jest NULL, jeśli dynamiczny rzut na referencję nie powiedzie się, zostanie wyrzucony wyjątek bad_cast. Gdy nie zawodzi, dynamiczny cast zwraca wskaźnik lub odniesienie typu docelowego do obiekt do którego wyrażenia się odnosiło.

Reinterpret_cast

Reinterpretować cast po prostu rzuca jeden typ bitowo do drugiego. Dowolny wskaźnik lub integralny Typ można odlewać na dowolny inny z reinterpretowanym odlewem, łatwo pozwalając na niewłaściwe użycie. Np. z reinterpretacją obsady jeden może, niesłychanie, rzucić wskaźnik integer na wskaźnik string.

 72
Author: TJ Seabrooks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-01 18:00:19

Unikaj używania odlewów w stylu C.

C-style odlewy są mieszanką const i reinterpretować odlewów, i trudno znaleźć-i-zastąpić w kodzie. Programista aplikacji C++ powinien unikać c-style cast.

 23
Author: ugasoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-22 21:09:03

FYI, uważam, że Bjarne Stroustrup jest cytowany jako mówiący, że należy unikać odlewów w stylu C i że należy używać static_cast lub dynamic_cast, jeśli w ogóle jest to możliwe.

Barne Stroustrup ' S C++ style FAQ

Weź tę radę za co chcesz. Jestem daleki od bycia guru C++.

 21
Author: Jason Baker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-03 11:19:38

C-style rzuca conflate const_cast, static_cast i reinterpret_cast.

Szkoda, że C++ nie ma c-style. Odlewy C++ wyróżniają się prawidłowo (tak jak powinny; odlewy zazwyczaj wskazują na zrobienie czegoś złego) i odpowiednio rozróżniają różne rodzaje konwersji, które wykonują odlewy. Pozwalają również na pisanie podobnych funkcji, np. boost:: lexical_cast, co jest całkiem miłe z punktu widzenia spójności.

 10
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-26 13:38:35

dynamic_cast posiada sprawdzanie typu runtime i działa tylko z referencjami i wskaźnikami, podczas gdy static_cast nie oferuje sprawdzania typu runtime. Pełne informacje można znaleźć w artykule MSDNOperator static_cast.

 8
Author: Inisheer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-22 21:05:29

dynamic_cast obsługuje tylko typy wskaźników i referencji. Zwraca NULL jeśli rzut jest niemożliwy, jeśli typ jest wskaźnikiem lub rzuca wyjątek, jeśli typ jest typem odniesienia. W związku z tym, dynamic_cast może być użyty do sprawdzenia, czy obiekt jest danego typu, static_cast nie może (po prostu skończysz z nieprawidłową wartością).

Rzuty w stylu C (i inne) zostały omówione w pozostałych odpowiedziach.

 7
Author: larsmoa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-13 07:41:02