2015-04-24 36 views
15

Zastanawiam się, czy następujący kod prowadzi do nieokreślonego zachowania:Czy upcasting null pointer prowadzą do nieokreślonego zachowania

#include <cstddef> 
#include <cstdio> 

struct IA { 
    virtual ~IA() {} 
    int a = 0; 
}; 
struct IB { 
    virtual ~IB() {} 
    int b = 0; 
}; 
struct C: IA, IB {}; 

int main() { 
    C* pc = nullptr; 
    IB* pib = pc; 
    std::printf("%p %p", (void*)pc, (void*)pib); 
} 
+0

Generuje to '0 0' przy użyciu C++ 14: https: // ideone.com/iefRnb – EdChum

+0

Przykład z wielokrotnym dziedziczeniem (a więc regulacją wskaźnika) byłby jeszcze bardziej interesujący. – Quentin

+0

@Quentin Uzgodnione. Zaktualizuję pytanie. – Lingxi

Odpowiedz

7

Stroustrup omawia tę sprawę w sekcji 4.5 his 1989 multiple inheritance paper [PDF]:

Rozwiązanie jest opracowanie operacji konwersji (rzutowania) na test dla wartości wskaźnika 0 [...]

Dodatkowa złożoność i czas działania w czasie wykonywania to test i przyrost.

Implementacja sprawdza jawnie wartości puste i zapewnia, że ​​wynik rzutowania nadal będzie wartością zerową. Było tak w C++ 98 i nie zmieniło się w C++ 11 i nullptr.

Jest to szczególnie ważne w przypadku wielu klas bazowych, w których rzutowanie z klasy pochodnej na jedną z klas bazowych może wymagać zmiany rzeczywistej wartości wskaźnika.

W twoim przykładzie układ C w pamięci zawiera najpierw bajty dla IA, a następnie bajty dla IB. Przesyłanie do IA jest trival, jako wskaźnik do początku C będzie również wskazywać na początek części IA z C. Przesyłanie do wersji IB wymaga zmiany wskaźnika C o rozmiar IA. Wykonanie tej zmiany w przypadku nullptr doprowadziłoby do pojawienia się wskaźnika zerowego po rzutowaniu, stąd specjalne traktowanie wartości zerowych.

jako pointed out by aschepler, w odpowiedniej sekcji w standardzie to [conv.ptr] §4.10:

prvalue typu „wskaźnik do cvD”, gdzie D jest typu klasy może być skonwertowano do wartości prod. Typu "wskaźnik do cvB", gdzie B jest podstawową klasą [...] z dnia D. [...] Wynik konwersji jest wskaźnikiem do podobiektu klasy podstawowej pochodnego obiektu klasy. Wartość wskaźnika pustego o wartości jest przekształcana na wartość wskaźnika pustego typu docelowego.

+0

Twoja odpowiedź zapewnia cenny wgląd w implementację. Bardzo dobra robota! – Lingxi

+0

@Lingxi Dzięki :) Bardzo lubię ten papier od Stroustrup. Jest niezwykle przydatny do zrozumienia, jak kompilator faktycznie zajmuje się dziedziczeniem wielokrotnym pod maską. – ComicSansMS

+0

Najlepiej byłoby, gdybyś mógł zaktualizować swoją odpowiedź z odniesieniem do standardu (np. Co zrobiła @aschepler). Zrozumienie zarówno pedantycznego standardu, jak i niektórych praktycznych zagadnień związanych z wdrażaniem jest dobre. – Lingxi

8

Upcasting pustego wskaźnika jest dobrze zdefiniowany, aby dać wam innego pustego wskaźnika:

4.10p3:

prvalue typu "wskaźnik do cvD", gdzie jest D typ klasy, można przekonwertować na wartość typu "wskaźnik do cvB", gdzie B jest klasą podstawową z . ... Wartość wskaźnika pustego jest konwertowana na wartość wskaźnika pustego typu docelowego.