2011-02-10 10 views
14

Mam mieszane uczucia na temat static_cast, ponieważ jest to najbezpieczniejsza dostępna wersja C++, ale umożliwia jednocześnie bezpieczną i niebezpieczną konwersję, więc musisz znać kontekst, aby powiedzieć, czy jest rzeczywiście bezpieczny lub może prowadzić do UB (np. podczas rzucania do podklasy).Czy static_cast jest niewłaściwie używany?

Dlaczego więc nie ma bezpieczniejszej, wyraźnej obsady? Oto przykład, w którym może być przydatny. W COM, muszą powrócić wskaźnik interfejsu jako void** ppv, więc „trzeba” rzucić jawnie

*ppv = (IInterface*) this; 

który następnie zasugerował, aby zostać zastąpione przez bezpieczniejsze C++ oddanych

*ppv = static_cast<IInterface*>(this); 

Ale to robi ma sens, by zrobić tutaj nawet static_cast? this ma klasy, która wywodzi się od IInterface, więc można po prostu napisać

IInterface* p = this; // implicit conversion to base, safe for sure 
*ppv = p; 

lub użyć pomocnika jak

template<class T, class U> 
T implicit_cast(U p) { return p; } 

*ppv = implicit_cast<IInterface*>(this); 

tak, czy to prawda, że ​​static_cast bywa nadużywane i może (powinien?) być zastąpiony przez ten implicit_cast w niektórych przypadkach, czy coś mi brakuje?

EDYCJA: Wiem, że a cast is required in COM, ale nie musi to być static_cast, wystarczy niejawny rzut.

+1

Uwaga: Patrz http://stackoverflow.com/a/869597 za "prawo" realizacji 'implicit_cast' (i ładnym wyjaśnieniem). –

Odpowiedz

5

W tym konkretnym przypadku uważam, że zawsze wiadomo, że odlewanie będzie w górę, a zatem static_cast powinno być całkowicie bezpieczne.

Wygląda na to, że używanie twojego implicit_cast prawdopodobnie byłby bezpieczniejsze i pozwala jawnie wybrać, do której klasy podstawowej chcesz niejawnie rzutować (co jest najwyraźniej wymagane dla COM).

Zrobiłem szybki test z g ++, a implicit_cast rzeczywiście zwraca różne adresy dla różnych klas bazowych zgodnie z oczekiwaniami.

Należy jednak zauważyć, że pod względem pierwszego zdania twierdzę, że dynamic_cast jest w rzeczywistości bezpieczniejsze niż static_cast, ponieważ zwróci wartość zerową lub rzut, jeśli rzutowanie nie może zostać zakończone. W przeciwieństwie do tego, static_cast zwróci prawidłowy wskaźnik i pozwoli ci kontynuować, dopóki twój program nie rozwinie się w przyszłości, nie połączony z oryginalnym złym rzutem.

Program testowy:

#include <iostream> 

class B1 
{ 
public: 
    virtual ~B1() {} 
}; 

class B2 
{ 
public: 
    virtual ~B2() {} 
}; 

class Foo : public B1, public B2 
{ 
}; 

template<class T, class U> 
T implicit_cast(U p) { return p; } 

int main() 
{ 
    Foo* f = new Foo; 
    void **ppv = new void*; 

    *ppv = implicit_cast<B1*>(f); 
    std::cout << *ppv << std::endl;; 
    *ppv = implicit_cast<B2*>(f); 
    std::cout << *ppv << std::endl;; 

    return 0; 
} 
+0

Rzeczywiście, 'dynamic_cast' byłby bezpieczniejszy niż' static_cast', ale nie wspomniałem o tym, ponieważ nigdy nie widziałem, żeby był faktycznie używany (słyszałem o uderzeniu wydajności, ale nie wiem, jak duży jest). –

+2

@ 7vies: W Visual C++ jest to około 2 tysiące cykli procesora, a kompilator nie pochwyci cię, jeśli rzucisz obiekt COM na niewłaściwy typ - dostaniesz tylko zerowy wskaźnik w czasie wykonywania. – sharptooth

+0

@sharptooth: Tak, 'dynamic_cast' zdecydowanie nie jest najlepszą opcją w kontekście COM. Nie jestem nawet pewien, w jakiej sytuacji może to być dobra opcja - w większości przypadków można ją zastąpić polimorfizmem wykonawczym lub kompilacją, która byłaby bezpieczniejsza i szybsza w tym samym czasie. –

Powiązane problemy