Digamos que tengo una clase abstracta base B y cien clases que derivan de ella D1 ... D100. También tengo dos punteros (inteligentes) unique_ptr<B> p1, p2; que apuntan a dos instancias diferentes de los tipos Di y Dj. Quiero saber si los objetos que apuntan tienen el mismo tipo (es decir, si i es igual a j). ¿Hay una manera simple de hacer eso?

3
Valentin 19 oct. 2017 a las 22:50

3 respuestas

La mejor respuesta

Puede usar un tipo de letra RTTI, pero en general, es un mal diseño tener que usar dynamic_cast porque puede violar el principio de sustitución de liskov

std::unique_ptr<B> p1, p2;
if(typeid(*p1) == typeid(*p2)) // correction by Justin

O algo así usando name o hash_code

7
Antoine Morrier 19 oct. 2017 a las 19:58

Verificar C ++ RTTI es relativamente costoso y engorroso: considere agregar una etiqueta de evento en la clase base y verificar ese valor.

enum class EventTag {
    A, B
};

struct EventBase {
    const EventTag tag;
    EventBase(EventTag tag) : tag(tag) {
    }
};

struct EventA : public EventBase {
    EventA() : EventBase(EventTag::A) {
    }
};

struct EventB : public EventBase {
    EventB() : EventBase(EventTag::B) {
    }
};

if (thisEvent->tag == thatEvent->tag) {
    // stuff
}

Una unión en lugar de una jerarquía de clases también podría hacerlo por usted.

5
zneak 19 oct. 2017 a las 20:28

Esto puede parecer un poco complicado al principio, pero puede garantizar la operatividad de lo que está buscando en términos de comparaciones. Hay un poco más de sobrecarga en este enfoque en mantenibilidad, pero asegurará una comparación adecuada sin demasiada sobrecarga de ejecución en lo que respecta a la ejecución de tiempo.

#include <string>
#include <iostream>
#include <memory>

class AbstractBase {
public:
    enum DerivedTypes {
        DERIVED_A,
        DERIVED_B
    };

protected:
    DerivedTypes type_;

public:
    explicit AbstractBase( const DerivedTypes type ) : type_( type ) {}

    virtual void print() = 0;

    DerivedTypes getType() const {
        return type_;
    }   

    bool operator==( const AbstractBase& other ) {
        return this->type_ == other.getType();
    }
};

class DerivedA : public AbstractBase {
public:
    const int x = 5;

public:
    explicit DerivedA( const DerivedTypes type = DerivedTypes::DERIVED_A ) : AbstractBase( type ) {}

    virtual void print() override {
        std::cout << "DerivedA\n";
    } 
};

class DerivedB : public AbstractBase {
public:
    const float y = 1.5f;

public:
    explicit DerivedB( const DerivedTypes type = DerivedTypes::DERIVED_B ) : AbstractBase( type ) {}

    virtual void print() override {
        std::cout << "DerivedB\n";
    }
};

int main() {
    std::unique_ptr<DerivedA> p1 = std::make_unique<DerivedA>( DerivedA() );
    std::unique_ptr<DerivedB> p2 = std::make_unique<DerivedB>( DerivedB() );

    p1->print();
    p2->print();

    if ( *p1 == *p2  ) {
        std::cout << "pointers are the same\n";
    } else {
        std::cout << "pointers are not the same\n";
    }

    std::unique_ptr<DerivedA> p3 = std::make_unique<DerivedA>( DerivedA() );
    std::unique_ptr<DerivedA> p4 = std::make_unique<DerivedA>( DerivedA() );

    p3->print();
    p4->print();

    if ( *p3 == *p4 ) {
        std::cout << "pointers are the same\n";
    } else {
        std::cout << "pointers are not the same\n";
    }

    return 0;
}

El costo adicional aquí de la mantenibilidad depende de cuántas clases derivadas hay. Si este patrón de diseño se usa desde el principio, es bastante fácil de seguir. Si uno tiene que modificar una cantidad numerable de clases, llevará algún tiempo y esfuerzo modificar la base de código existente.

La idea aquí es que la clase base abstracta mantiene una variable interna del tipo derivado, tiene un captador para su tipo derivado y un operador de comparación == sobrecargado que compara los tipos. El constructor se ve obligado a tomar su tipo enumerado, así como todos los tipos derivados. También predeterminé los tipos derivados para que no tenga que pasar a sus constructores cada vez que se crea una instancia de un objeto de clase. Esta es la sobrecarga de la mantenibilidad.

La simplicidad de esto entra en juego al comparar punteros: punteros inteligentes de múltiples tipos derivados. Cuando observa la función principal anterior, puede ver que era lo suficientemente simple como para desreferenciar los punteros inteligentes y el operador == sobrecargado de la clase base se ocupa de los negocios.

Si uno necesita usar operator==() para comparar realmente las diferencias numéricas entre clases, entonces esto puede modificarse para usar un método público en la clase base que compare los tipos y devuelva un valor bool en lugar del operador sobrecargado.

0
Francis Cugler 19 oct. 2017 a las 22:14