2012-09-20 24 views
9

¿Alguien puede explicar el comportamiento del siguiente código?Addresses, reinterpret_cast y multiple inheritance

  1. ¿Por qué tenemos b = 3 en el primer caso, es decir b2 == &d es cierto?
  2. ¿Por qué está bien en el caso 2? He impreso las direcciones de b2 y d, y son diferentes.
#include <iostream> 

using namespace std; 

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; 
    B *b2 = &d; 

    cout << &d << endl; 
    cout << b2 << endl; 

    const int b = (b2 == &d) ? 3 : 4; ///Case1: b = 3; 
    const int c = (reinterpret_cast<char*>(b2) == reinterpret_cast<char*>(&d)) ? 3 : 4; //Case 2: c = 4; 

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

    return 0; 
} 
+0

+1, simplemente porque este es un buen ejemplo e ilustra bastante bien los efectos de la herencia múltiple en los punteros – ltjax

+0

+1. gran ejemplo, especialmente sin virtuales para enviar gente realmente a través del timbre. – WhozCraig

Respuesta

9

d es de tipo C. Al convertir un puntero a C a un puntero a B, se ajusta de manera que apunte a la subobjeto B de C (se necesita tal ajustement en general con herencias múltiples, si no se necesitaba ninguna para B, habría una para A como C heredaría tanto de A como de B). Por lo tanto, en el tiempo de asignación y comparación, el ajuste está hecho.

En las otras dos ocasiones, & d se convierte a void * (implícitamente) o char * (con reinterpret_cast) y no se realiza ningún ajuste (solicitó explícitamente que no se ajuste con reinterpret_cast y no hay motivo para hacer un ajuste al convertir a void *, simplemente complicaría el viaje de ida y vuelta sin una buena razón, volvería a tener un resultado similar para A) por lo que la representación es diferente.

Por cierto, si ha usado reinterpret_cast<B*>(&d), no se habría realizado ningún ajuste de nuevo, pero si utiliza el resultado como B *, podría haber problemas rápidamente.

3

En el caso 1, la comparación arroja automáticamente el puntero C a un puntero B. En este caso, esto significa que la dirección real cambia, ya que usa herencia múltiple y B es el segundo en su lista de clases base. Para ser más específico, el puntero debe estar compensado por (al menos) tamaño de (A). En el segundo caso, sin embargo, no se realiza dicha conversión automática, por lo que el "prefijo A" hace que los dos punteros sean desiguales.

+0

No existe el "molde automático". Un elenco es una conversión explícita, existe en el código fuente. – curiousguy

+0

¿Qué quieres decir con eso? Claramente hay un aumento en el caso 1, pero no es explícito. – ltjax

+0

Un elenco que no es explícito simplemente no es un elenco. Un elenco puede ser de estilo C, estilo funcional o nuevo estilo: '(T) x',' T (x) ',' static_cast (x) '... – curiousguy