Search code examples
c++ooppolymorphismobject-type

Polymorphism and get object type in C++


Let's say that I have 4 classes, B,C,D,E that inherit from A(abstract base class). Also I have a container (std::vector) of type A* whose contents point to either of B,C,D,E objects. Here are some rules: If a B object and a C object interact, they get removed from the vector and in their place a D object gets created.

Also, C + D = E

Now, suppose that I randomly choose one of said vector contents; how would I go about knowing which object is of which type, in order to implement an interaction mechanic?

NOTE: I do not wish to use the typeid operator, a dynamic cast or flags. Any other solutions?

Here is some code

#include <iostream>

class A {
protected:
    int v;
public:
    A(){}
    ~A(){}
};

class B :public A {
public:
    B(){}
    ~B(){}
};

class C : public A {
public:
    C(){}
    ~C(){}
};

class D : public A {
public:
    D(){}
    ~D(){}
};

class E : public A {
public:
    E(){}
    ~E(){}
};

int main()
{
    std::vector<A*> container;
    return 0;
}

How would I implement the interact function(s) ?


Solution

  • You may use virtual functions to do the multiple dispatch

    struct B;
    struct C;
    struct D;
    struct E;
    
    struct A
    {
        virtual ~A() = default;
    
        virtual std::unique_ptr<A> interactWithA(const A&) const = 0;
    
    //protected:
        virtual std::unique_ptr<A> interactWithB(const B&) const = 0;
        virtual std::unique_ptr<A> interactWithC(const C&) const = 0;
        virtual std::unique_ptr<A> interactWithD(const D&) const = 0;
        virtual std::unique_ptr<A> interactWithE(const E&) const = 0;
    };
    
    // Your interact rules
    
    template <typename LHS, typename RHS>
    std::unique_ptr<A> interact(const LHS&, const RHS&) { return nullptr; }
    
    // Note that definitions and declarations must be split in reality
    // to be able to compile it
    std::unique_ptr<A> interact(const B&, const C&) { return std::make_unique<D>(); }
    std::unique_ptr<A> interact(const C&, const D&) { return std::make_unique<E>(); }
    // Maybe the reflexive case, C/B D/C ?
    
    
    // The derived classes
    struct B : A
    {
        std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithB(*this); }
    
        // Even if code look similar for other inherited class
        // the difference is in the runtime type of the objects are known.
        std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
    };
    
    struct C : A
    {
        std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithC(*this); }
        std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
    };
    
    struct D : A
    {
        std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithD(*this); }
        std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
    };
    
    struct E : A
    {
        std::unique_ptr<A> interactWithA(const A& a) const override { return a.interactWithE(*this); }
        std::unique_ptr<A> interactWithB(const B& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithC(const C& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithD(const D& rhs) const override { return interact(rhs, *this); }
        std::unique_ptr<A> interactWithE(const E& rhs) const override { return interact(rhs, *this); }
    };
    

    and then

    std::vector<std::unique_ptr<A>> v /* =  .. */;
    
    auto a = v[i]->interactWithA(*v[j]);
    if (a) {
        // Remove v[i] and v[j]
        // Insert a
    }