2013-05-26 12 views
5

Poniższy kod podaje dane wyjściowe jako 136. Nie mogłem jednak zrozumieć, w jaki sposób pierwsze dwa porównania adresów są równe. Doceniamy każdą pomoc, aby to zrozumieć. Dziękuję Ci.interpretacja adresów obiektów za pomocą reintepret_cast

#include <iostream> 

class A 
{ 
public: 
    A() : m_i(0){ } 
protected: 
    int m_i; 
}; 

class B 
{ 
public: 
    B() : m_d(0.0) { } 
protected: 
    double m_d; 
}; 

class C : public A, public B 
{ 
public: 
    C() : m_c('a') { } 
private: 
    char m_c; 
}; 

int main() 
{ 
C d; 
A *b1 = &d; 
B *b2 = &d; 

const int a = (reinterpret_cast<char *>(b1) == reinterpret_cast<char *>(&d)) ? 1 : 2; 
const int b = (b2 == &d) ? 3 : 4; 
const int c = (reinterpret_cast<char *>(b1) == reinterpret_cast<char *>(b2)) ? 5 : 6; 

std::cout << a << b << c << std::endl; 

return 0; 
} 
+1

Dlaczego używasz tutaj 'reinterpret_cast'? Czego oczekujesz od używania 'char *' dla typu wskaźnika na twojej architekturze? –

+0

nie jest to aplikacja działająca w czasie rzeczywistym. To było z testu C++. Chcę tylko zrozumieć, jak reinterpret_cast interpretuje adresy w tym konkretnym przypadku. – irappa

Odpowiedz

1

Podczas korzystania z dziedziczenia wielokrotnego, tak jak w przykładzie, pierwsza klasa bazowa i klasa pochodna mają ten sam adres bazowy. Dodatkowe klasy, z których dziedziczysz, są ułożone w kolejności w oparciu o wielkość wszystkich poprzednich klas. Wynik porównania jest prawdziwy, ponieważ adres bazowy d i b1 jest taki sam.

W twoim przypadku, jeśli rozmiar A wynosi 4 bajty, to B rozpocznie się od adresu podstawowego A + 4 bajty. Gdy wykonasz B *b2 = &d;, kompilator oblicza przesunięcie i odpowiednio dostosowuje wartość wskaźnika.

Po przeprowadzeniu b2 == &d niejawna konwersja z typu "C" na typ "B" jest wykonywana na d przed wykonaniem porównania. Ta konwersja dostosowuje przesunięcie wartości wskaźnika w taki sam sposób, jak w przypadku przypisania.

+0

dziękuję. Ale co powiesz na b2 == & d? – irappa

+0

Zaktualizowano odpowiedź, dodając informację, dlaczego tak się złożyło. –

0

To dość typowe dla klasy pochodnej (np C tutaj), która zostanie określona w pamięci tak zaczyna się jego dwóch klas bazowych (A i B), więc adres instancji typu C byłby identyczny z adres instancji jego pierwsza klasa podstawowa (tj. A).

0

W tego rodzaju dziedziczenia (gdy virtual nie jest zaangażowany,) każda instancja C będą miały następujący układ:

  • Po pierwsze, nie będą wszyscy członkowie A (który jest tylko m_i, A4 -bajtowych całkowita)
  • drugie będą wszyscy członkowie B (co jest tylko m_d, 8-bajtowy podwójna)
  • Ostatni będą wszyscy członkowie C sobie, co jest po prostu postać (1 bajt, m_c)

Kiedy rzucisz wskaźnik do instancji C do A, ponieważ A jest pierwszym rodzicem, nie regulacja odbywa adres, a wartość liczbowa z dwóch wskaźników będzie taka sama. Dlatego pierwsze porównanie ocenia się na true. (Zauważ, że robi reinterpret_cast<char *>() na wskaźnik nigdy nie powoduje korektę, więc to zawsze daje wartość liczbową wskaźnika. Casting do void * miałoby ten sam efekt i jest prawdopodobnie bezpieczniejsze dla porównania.)

Casting wskaźnik do instancji od C do Bbędzie spowodować regulację wskaźnika (o 4 bajtów), co oznacza, że ​​wartość liczbowa b2 będzie nie równa &d. Jednak po bezpośrednim porównaniu b2 i &d kompilator automatycznie generuje rzutowanie dla &d na B *, które dostosuje wartość liczbową o 4 bajty. To jest powód, dla którego drugie porównanie również ocenia się na true.

Trzeci porównanie powrotu false, ponieważ, jak wspomniano, odlewanie wskaźnik do wystąpienia C do A lub B mają różne wyniki (rzutowania A * nie do regulacji, podczas odlewania do B * nie).

+0

Układ zależy od kompilatora. To, co opisałeś, jest typowe, ale nigdzie nie jest nakazane. – EJP

+0

@EJP Dobrze, jesteś! Ale OP poprosił kogoś o wyjaśnienie zachowania, które widzi na jego skompilowanym kodzie. I właśnie to robiłem. Nie twierdzę, że jest to wymagane przez normę; Po prostu wyjaśniam, jak wyjście jego programu stało się tym, czym jest. – yzt

Powiązane problemy