2012-04-02 17 views
13

considerar dos punteroscómo comprobar si dos punteros apuntan al mismo objeto o no?

A* a; 
B* b; 

Tanto A como B son clases polimórficas. ¿Cómo verificar si a y b apuntan al mismo objeto o no?

Más precisamente, especifiquemos un punto a y un punto para el mismo objeto si existe algún objeto d de tipo D tal que tanto * a como * b están en algún lugar de la jerarquía de clases de d.

propondría la siguiente solución:

dynamic_cast<void*>(a) == dynamic_cast<void*>(b) 

De hecho, según la norma,

dynamic_cast<void*>(v) 

rendimientos “un puntero al objeto más derivada apuntada por v (n3242.pdf. : § 5.2.7 - 7). Si el más derivado para ambos es el mismo objeto, los punteros apuntan al mismo objeto.

Estoy bastante seguro de que siempre debería funcionar correctamente desde el punto de vista práctico. Pero teóricamente, a primera vista, la igualdad propuesta parece producir falsos positivos, por ejemplo, en caso de que b señale al primer miembro de A (no al antepasado de A). Aunque es prácticamente imposible obtener direcciones iguales para A y su miembro, ya que el puntero de tabla virtual de A debe ubicarse antes que este miembro, el estándar no ordena tablas virtuales y no dice nada sobre el diseño de clase.

Por lo tanto, mis preguntas son:

  1. es la solución propuesta correcta desde el punto de vista estándar?

  2. ¿Existen advertencias sobre herencia privada (protegida) o cv-qualification?

  3. ¿Hay mejores soluciones?

[EDIT]

I tratado de presentar algún ejemplo que ilustra un escenario relativamente complejo. En este caso, el crosscasting dinámico y el fundido estático son ambiguos.

// proposed impplementation: 
template<typename P, typename Q> 
bool test_ptrs(const P* p, const Q* q) 
{ 
    return (dynamic_cast<const void*>(p) == dynamic_cast<const void*>(q)); 
} 


struct Root 
{ 
    virtual ~Root(){}; 
}; 

struct A: public Root // nonvirtually 
{ 
}; 

struct B: public Root // nonvirtually 
{ 
}; 

struct C: public A, B // nonvirtual diamond started with Root 
{ 
    Root another_root_instance; 
}; 

int main() 
{ 
    C c; 

    A* pa= &c; 
    B* pb= &c; 

    bool b = (dynamic_cast<void*>(pa) == dynamic_cast<void*>(pb)); 

    Root* pra= dynamic_cast<Root*> (pa); 
    Root* prb= dynamic_cast<Root*> (pb); 

    //Root* prc= dynamic_cast<Root*> (&c); // runtime error, ambiguous cast 
    Root* prr= dynamic_cast<Root*>(pra); 

    Root* pcar= dynamic_cast<Root*>(pra); 
    Root* pcbr= dynamic_cast<Root*>(prb); 

    if(
     test_ptrs(pa, pb) 
     && test_ptrs(pra, prb) 
     && !test_ptrs(pa,&c.another_root_instance) 
    ) 
    { 
    printf("\n test passed \n"); 
    } 
} 
+11

¿Por qué no 'a == b'? – iammilind

+1

@iammilind: A y B pueden ser clases base para algunos D pero no relacionados entre sí – user396672

+2

+1 para @iammilind: ¡los viejos son los mejores! –

Respuesta

1

Estaba tratando de resolver esto comparando la dirección a la que apuntan estos punteros.

  • Dirección está apuntando a cambios según el tipo de puntero.

Por lo tanto, teóricamente, podemos decir como

a * puntos * b al mismo objeto si existe algún objeto c de tipo C tal que tanto * A y * b están en algún lugar en la clase jerarquía de C."

Lógicamente

tenemos que volver a la afirmación anterior como " a * puntos * b al mismo objeto pero tiene que propia zona de acceso en la memoria de obj c de tipo C tal que tanto * a * y b están en algún lugar en la jerarquía de clases de C. ""

struct Aa { int a; Aa() {a= 0;} };

struct Bb 
{ int b; 
    Bb() { b= 0;} 
}; 
struct C: Aa, Bb {  
}; 

C c; 
Aa *a1 = &c; 
Aa *a2 = &c; 
Bb *b1 = &c; 
Bb *b2 = &c; 

cout << &c << "\t"<< &(*a1)<<"\t"<< &(*a2)<<endl; 
cout << &c << "\t"<< &(*b1)<<"\t"<< &(*b2)<<endl; 

de salida:

  • & c 0x0012fd04
  • & (* a1) 0x0012fd04
  • & (* a2) 0x0012fd04
  • & (* b1) 0x0012fd08
  • & (* b2) 0x0012fd08

Aunque esto no resolverá su problema, tenemos un punto para inferir aquí.

1

Parecería a mí la forma menos mal olor que lidiar con esto es la introducción de una clase base para un & B:

#include <iostream> 

struct Base 
{ 
    virtual ~Base() {}; 
}; 

struct A : public virtual Base 
{ 
    int a; 
    virtual ~A() {}; 
    virtual void afunc() {}; 
}; 



struct B : public virtual Base 
{ 
    int b; 
    virtual ~B() {}; 
    virtual void bfunc() {}; 
}; 

struct C: A, B 
{}; 

int main() 
{ 
    C c; 
    A *a = &c; 
    B *b = &c; 

    std::cout << "a* == " << &(*a) << std::endl; 
    std::cout << "b* == " << &(*b) << std::endl; 
    std::cout << "a == b == " << ((void*)a == (void*)b) << std::endl; 

    Base* ba = a; 
    Base* bb = b; 

    std::cout << "ba* == " << &(*ba) << std::endl; 
    std::cout << "bb* == " << &(*bb) << std::endl; 
    std::cout << "ba == bb == " << (ba == bb) << std::endl; 

    return 0; 
} 
+0

Estoy de acuerdo en que es menos maloliente, aunque, en términos estrictos, no es una respuesta :). – user396672

0

ya que con dynamic_cast También se puede convertir "de lado" en la jerarquía de tipos, yo sugeriría:

(b != nullptr? dynamic_cast<B*>(a) == b : a == nullptr) 

If a puntos a algún subobjeto en el comienzo de *b, entonces dynamic_cast<B*>(a) necesariamente devolverá un puntero nulo (porque no hay manera de un B se contiene a sí mismo). Por lo tanto, si b no es un puntero nulo, dynamic_cast<B*>(a) == b tendrá éxito solo si ambos comparten la misma clase derivada. El caso de que b sea un puntero nulo debe tratarse específicamente porque si a no es nulo, pero no apunta a una clase derivada de B, la prueba dynamic_cast fallará.

Sin embargo, hay algunas situaciones que implican herencia múltiple en las que esta solución dará un falso negativo (a diferencia de su solución que nunca da falsos negativos, pero puede dar falsos positivos). Sin embargo, las jerarquías de clase en las que esto podría suceder son jerarquías que diría que no debería crear de todos modos (es decir, la misma clase derivada que contiene múltiples bases indirectas de tipo B). Puede reducir el número de falsos negativos volviendo a probar mediante el intercambio del rol de a y b (solo si ambosA y B son ambiguos en la clase más derivada, la prueba fallará).

También puede combinar su y mi ensayo para dar tres resultados:

  • Ambas pruebas tienen éxito: Los punteros son sin duda al mismo objeto (o nula) Noth.
  • Ambas pruebas fallan: los punteros definitivamente no son para el mismo objeto.
  • Solo mi prueba falla: su prueba ha dado un positivo falso, o mi prueba ha dado un resultado negativo falso. No se puede decir con certeza si ambos son el mismo objeto, pero al menos se puede decir que no se puede saber.
Cuestiones relacionadas