2011-02-03 8 views
5

Niedawno znalazłem świetny przykład, dlaczego rzuty w stylu C są złe. Zaczynamy następnym klasa implementująca wiele interfejsów COM (mam dwie dla zwięzłości, ale nie może być dziesięć w prawdziwym życiu):Jakie przykładowe przykłady sĘ ... tam pokazywane rzuty w stylu C sĘ ... złe?

class CMyClassInitial : public IInterface1, public IInterface2 { 
    //declarations omitted 
}; 

HRESULT CMyClassInitial::QueryInterface(REFIID iid, void** ppv) 
{ 
    if(ppv == 0) { 
     return E_POINTER; 
    } 
    *ppv = 0; 
    if(iid == __uuidof(IUnknown) || iid == __uuidof(IInterface1)) { 
     *ppv = (IInterface1*)this; 
    } else if(iid == __uuidof(IInterface2)) { 
     *ppv = (IInterface2*)this; 
    } else { 
     return E_NOINTERFACE; 
    } 
    AddRef(); 
    return S_OK; 
} 

Powyższa implementacja wykorzystuje C-odlewane dla adjusting pointers to account for multiple inheritance. Działają nawet tak, jakby wartość wskaźnika została odpowiednio dostosowana.

Teraz kopiuj-wklej (lub powinienem powiedzieć ponowne wykorzystanie kodu z?) Ta sama QueryInterface() wdrożenie do innego bardzo podobnej klasy.

class CMyClassModified : public IInterface1 { 
    //declarations omitted 
}; 

i pozostawić wdrożenie tak samo. Nowa klasa nie dziedziczy z IInterface2 już jednak

} else if(iid == __uuidof(IInterface2)) { 
*ppv = (IInterface2*)this; 
} 

skompiluje dobrze i C-style cast będzie działać jako reinterpret_cast - this wartość wskaźnika zostaną skopiowane bez zmian. Osoba dzwoniąca uzyska wskaźnik do obiektu, który w rzeczywistości nie implementuje IInterface2 - prosto do niezdefiniowanego zachowania. Takie problemy mogą być trudne do wykrycia w ogromnej bazie danych i gdy istnieje wiele (nie dwa, jak w moim przykładzie) interfejsów.

Jeśli static_cast użyto że by się nie stało - kompilator będzie emitować błąd próbuje skompilować

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

IMO to jest wystarczająco ostry przykład jak za pomocą odlewane stylu C może spowodować poważne problemy.

Jakie są inne przykłady?

+0

Wielki haczyka, ale nie jestem całkowicie pewien, że to nadaje się do SO. Wydaje się bardzo dyskusyjny. W najlepszym razie jest to wiki społeczności. – tenpn

+0

@tenpn: Nie widzę, co można tutaj omówić - tylko przykład strzelania sobie w nogę w C++. – sharptooth

+1

@ Sharppt, ale to nie jest pytanie, prawda? – tenpn

Odpowiedz

2

This FAQ item sumuje wszystko, dlaczego c-casts są złe.

Każda c-cast jest potencjalną bombą, ponieważ ukrywa ostrzeżenia i błędy konwersji, wyciszając kompilator.

Ponieważ chcieliśmy przykład, tutaj jest:

int main() 
{ 
    float a = 0.123; 
    double *b = (double*) &a; 
    *b = 0.123; 
} 
+0

Dobry artykuł z najczęściej zadawanymi pytaniami, ale dotyczy on w zasadzie odlewów, a nie konkretnych odlewów. – sharptooth

+0

@sharptooth Zgaduję, że masz rację. Jednak każdy przyzwoity przewodnik po stylach kodowania w stylu C++ podpowie Ci, aby nie używać rzutów w stylu c. –

1

bardzo prosty przykład:

class ClassB;//only forward declaration, no real declaration included 

Class A * a; 
Class B * b; 
a = (ClassA *)b; 

Obsada zawsze będzie cicho udane, jeśli jest tylko do przodu deklaracja ClassB. Nie obchodzi, czy klasa B pochodzi z klasy A. I to będzie też źle, gdy ClassB nie tylko pochodzi od ClassA:

class ClassB:public SomeOtherClass, public ClassA {}; 
Powiązane problemy