2010-09-18 8 views
6

que tienen una clase base¿Cuál es la forma correcta de implementar la comparación para una clase base?

class Animal 

con funciones virtuales puras, y un conjunto de clases derivadas

class Monkey : public Animal 
class Snake : public Animal 

Quiero poner en práctica una operación de comparación de modo que, si me encuentro con dos punteros a los animales en mi código

Animal* animal1 
Animal* animal2 

Puedo compararlos entre sí. La comparación debería dar como resultado falso, si animal1 y animal2 son de diferentes clases derivadas. Si son de la misma clase derivada, se debe devolver la salida del operador de comparación.

¿Alguien me puede indicar una buena forma de implementar esto?

Respuesta

4

Guau, muchas de las otras respuestas fueron totalmente innecesarias. dynamic_cast- existe, úsala.

class Animal { 
public: 
    virtual bool operator==(const Animal& other) = 0; 
    virtual ~Animal() = 0; 
}; 
template<class T> class AnimalComp : public Animal { 
public: 
    virtual bool operator==(const Animal& ref) const { 
     if (const T* self = dynamic_cast<const T*>(&ref)) { 
      return ((T*)this)->operator==(*self); 
     } 
     return false; 
    } 
    virtual bool operator!=(const Animal& ref) const { 
     if (const T* self = dynamic_cast<const T*>(&ref)) { 
      return ((T*)this)->operator!=(*self); 
     } 
     return true; 
    } 
}; 
class Monkey : public AnimalComp<Monkey> { 
public: 
    virtual bool operator==(const Monkey& other) const { 
     return false; 
    } 
    virtual bool operator!=(const Monkey& other) const { 
     return false; 
    } 
}; 
class Snake : public AnimalComp<Snake> { 
public: 
    virtual bool operator==(const Snake& other) const { 
     return false; 
    } 
    virtual bool operator!=(const Snake& other) const { 
     return false; 
    } 
}; 

Editar: ¡Arco antes de mi implementación automática de plantillas!

Editar edición: Una cosa que hice fue olvidar etiquetarlos como const, lo cual fue incorrecto de mi parte. ¡No me disculparé por no haberlo hecho! = Como, seamos sinceros, implementarlo es un completo juego de dados.

Más ediciones: Jesucristo, chicos, este no es un ejemplo de cómo escribir! = O ==, es un ejemplo de cómo usar el CRTP.Si no le gusta cómo elegí implementar my! = O ==, puede demandar.

+0

Sí, mejor con la sobrecarga del operador ==. – imaginaryboy

+0

Error menor, necesita ser 'const monkey * p = dynamic_cast (& other)' y 'return * this == * p' ... las referencias no son convertibles a' bool'. – imaginaryboy

+0

@imaginaryboy: Sí, lo arreglé para una solución mucho mejor. – Puppy

3

Como no hay información de tipo estático asociada con los dos punteros, necesitará usar RTTI. Puede comparar los resultados del tipo typeid operator para determinar si los objetos son del mismo tipo.

Una alternativa sería agregar su propio tipo de ID a la clase Animal. Agregue otra función virtual y las clases derivadas devuelvan algo que identifique de manera única el tipo. Puede usar una enumeración, o tal vez el nombre del tipo como una cadena. Si puede usarlo, sin embargo, RTTI sería mucho mejor en mi humilde opinión.

+0

+1. Además, RTTI incurre en una sobrecarga de tiempo de ejecución (obviamente), aunque no demasiado. Si su aplicación tiene toneladas de clases y solo necesita RTTI en solo unos pocos lugares, iría con su propia solución mediante una función dedicada para la identificación del tipo. Sin embargo, si el uso de RTTI es algo común, definitivamente debes usarlo usando tu propio método. –

+1

Creo que el envío doble podría ser una mejor forma de hacerlo que manipular manualmente los objetos 'std :: type_info'. ¿Qué vas a hacer una vez que sepas que es el tipo correcto de todos modos? Todavía tendría que convertir el puntero a un puntero de clase derivado. Y dado que C++ solo tiene un RTTI mínimo, probablemente tendrías que cambiar de tipo. Y un cambio sobre un tipo suele ser un fuerte indicio de que alguien mejor ha utilizado las funciones 'virtuales'. – sbi

+0

@sbi De acuerdo, en realidad. He pasado demasiado tiempo programando a regañadientes "The Java Way" (tm). Obviamente, ha afectado mi C++. –

4

Una forma de implementar esto, es usar doble de despacho para diferenciar entre 'misma clase' y 'diferentes clases':

class Monkey; 
class Snake; 

class Animal { 
public: 
    virtual bool compare_impl(const Animal*) const { return false; } 
    virtual bool compare_impl(const Monkey*) const { return false; } 
    virtual bool compare_impl(const Snake*) const { return false; } 
    virtual bool compare(const Animal* rhs) const =0; 
}; 

class Monkey : public Animal { 
private: 
    /* Override the default behaviour for two Monkeys */ 
    virtual bool compare_impl(const Monkey*) const { /* compare two Monkey's */ } 
public: 
    /* Let overload-resolution pick the compare_impl for Monkey and let virtual dispatch select the override in the dynamic type of rhs */ 
    virtual bool compare(const Animal* rhs) const { return rhs->compare_impl(this); } 
}; 

class Snake : public Animal { 
private: 
    /* Override the default behaviour for two Snakes */ 
    bool compare_impl(const Snake*) const { /* compare two Snakes */ } 
public: 
    /* Let overload-resolution pick the compare_impl for Monkey and let virtual dispatch select the override in the dynamic type of rhs */ 
    virtual bool compare(const Animal* rhs) const { return rhs->compare_impl(this); } 
}; 
+0

¿Puede explicar exactamente la línea usando Animal :: compare_impl; ¿medio? Hasta ahora, he visto la palabra clave "usar" solo en "using namespace foo" – Hans

+0

@Hans: trae un identificador de clase base en el alcance de la clase derivada, evitando que una sobrecarga de clase derivada oculte la función de clase base. – sbi

+0

Sus declaraciones 'using' hacen dos cosas: 1) cambiar el acceso de la clase base 'compare_impl' de protected a private, y 2) hacer accesible Animal :: compare_impl dado que estaba oculto por las declaraciones' compare_impl' de la clase derivada . – imaginaryboy

0

Aquí hay un pequeño truco Yo uso (espero que pueda funcionar para ti también). agrego el siguiente método privado a los animales y anularlo en cada clase derivada (lo sé, es un poco de problemas, pero es más rápido que RTTI)

class Animal { 
protected: 

virtual const void* signature() const 
{ 
    static bool dummy; 
    return &dummy; 
} 
... 
} 


class Monkey : public Animal { 
private: 
virtual const void* signature() const 
{ 
    static bool dummy; 
    return &dummy; 
} 
... 
} 

ahora con el fin de ver si 2 punteros (a y B) son de la misma clase sólo para comprobar

a-> firma() == b-> firma()

en realidad no es una solución, que es un truco, pero funciona con sólo 2 Llamadas a métodos virtuales (1 para cada uno de los punteros) por lo que es bastante rápido.

Cuestiones relacionadas