Valentin
Valentin

Reputation: 1158

Check if two (smart) pointers point to the same derived class

Let's say I have a base abstract class B and a hundred classes which derive from it D1 ... D100. I also have two (smart) pointers unique_ptr<B> p1, p2; which point to two different instances of types Di and Dj. I want to know whether the objects they're pointing to have the same type (i.e. whether i equals j). Is there a simple way to do that?

Upvotes: 4

Views: 2506

Answers (3)

Francis Cugler
Francis Cugler

Reputation: 7915

This may seem a little convoluted at first but it can ensure the operability of what you are looking for in the terms of comparisons. There is a little more overhead to this approach in maintainability, but it will ensure proper comparison without too much overhead of execution in regards of time execution.

#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;
}

The added cost here of the maintainability depends on how many derived classes there are. If this design pattern is used from the beginning it is quite easy to follow. If one has to modify a numerable amount of classes it will take some time and effort to modify the existing code base.

The idea here is that the abstract base class keeps an internal variable of the derived type, has a getter for it's derived type & an overloaded == comparison operator that compares the types. The constructor is forced to take its enumerated type as well as all the derived types. I also defaulted the derived types so that it doesn't have to be passed to their constructors every time a class object is instantiated. This is the overhead of the maintainability.

The simplicity of this comes into play when comparing pointers - smart pointers of multiple derived types. When you look at the main function above you can see that it was simple enough to dereference the smart pointers and the base class's overloaded == operator takes care of business.

If one does need to use the operator==() to actually compare numerical differences between classes then this can be modified to use a public method in the base class that compares the types and returns a bool instead of the overloaded operator.

Upvotes: 0

zneak
zneak

Reputation: 138171

Checking C++ RTTI is relatively expensive and cumbersome: consider adding an event tag in the base class, and checking for that value.

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
}

A union instead of a class hierarchy could also do it for you.

Upvotes: 5

Antoine Morrier
Antoine Morrier

Reputation: 4078

You can use a RTTI typeid, but generally, it is a bad design to must have to use dynamic_cast because it may violate the liskov substitution principle

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

Or something like that using name or hash_code

Upvotes: 7

Related Questions