Search code examples
c++operator-overloadingvirtual

c++ overloading virtual + operator


#include <iostream>

class aa
{
public:
    aa(){}

    aa(aa& obj)
    {
        aa1 = obj.aa1;
    }

    virtual aa operator =(aa& obj)
    {
        aa1 = obj.aa1;
        return (*this);
    }

    virtual aa operator +(aa& obj)
    {
        aa1 += obj.aa1;
        return (*this);
    }

    int aa1;
};

class bb: public aa
{
public:
    bb():aa(){}

    bb(bb& obj)
    {
        bb1 = obj.bb1;
    }

    aa operator =(aa& obj)
    {
        aa::operator =(obj);
        bb b1 = dynamic_cast<bb&>(obj);
        bb1 = b1.bb1;       
        return (*this);
    }

    aa operator +(aa& obj)
    {
        aa::operator +(obj);
        bb b1 = dynamic_cast<bb&>(obj);
        bb1 += b1.bb1;
        return (*this);
    }

    int bb1;
};


int main()
{
    bb b1;
    bb b2;

    b1.bb1 = 1;
    b1.aa1 = 1;

    b2.bb1 = 2;
    b2.aa1 = 2;

    aa &a1 = b1;
    aa &a2 = b2;

    a1 = a2;
    b1 = dynamic_cast<bb&>(a1);
    b2 = dynamic_cast<bb&>(a2);

    std::cout<<b1.aa1<<";"<<b1.bb1;

    bb b3;
    b3.bb1 = 3;
    b3.aa1 = 3;

    aa &a3 = b3;

    aa &a4 = a2 + a3;
    b3 = dynamic_cast<bb&>(a4);

    return 0;
}

Output: 2;2 and then it crashes at line b3 = dynamic_cast<bb&>(a4); giving the error std::bad_cast at memory location 0x0012fdbc..

The reason I have found is the result of expression of a2+a3 is coming as object of type aa, and in the next statement we are trying to cast it to derived type object which is not valid, hence leading to exception. So my query is can we achieve the above intention i.e. aa &a4 = a2 + a3; with some changes in the above functions?


Solution

  • C++ has a single dispatch (based on virtual function). Polymorphic binary operators fall inevitably into the "dual dispatch" case, and hence cannot be implemented polymorphicaly by means of just simple virtual function.

    Also, since you are returning a value, every sub-classes information is lost.

    A more proper way to handle this kind of situation is to define a non-polymorphic handle that implements the operations and holds polymorphic types, delegating to them the operation execution.

    Like

    class handle
    {
    public:
        class aa;
        class bb;
    
        class root
        {
        public:
            virtual ~root() {}         //< required being this polymorphic
            virtual root* clone()=0;
    
            virtual handle add_invoke(const root& r) const=0; //resolve the 2nd argument
            virtual handle add(const aa& a) const=0;    //resolve the 1st argument
            virtual handle add(const bb& a) const=0;    //resolve the 1st argument
        };
    
        class aa: public root
        {
        public:
            aa(...) { /*set vith a value */ }
            aa(const aa& a) { /* copy */ }
            virtual root* clone() { return new aa(*this); }
    
            virtual handle add_invoke(const root& r) const 
            { return r.add(*this); }  //will call add(const aa&);
    
            virtual handle add(const aa& a) const
            { return handle(new aa(.../*new value for aa with (aa,aa)*/)); }
            virtual handle add(const bb& a) const
            { return handle(new bb(.../*new value for bb with(aa,bb)*/)); }
        };
    
        class bb: public root
        {
        public:
            bb(...) { /*set vith a value */ }
            bb(const bb& b) { /* copy */ }
            virtual root* clone() { return new bb(*this); }
    
            virtual handle add_invoke(const root& r) const
            { return r.add(*this); }  //will call add(const bb&);
    
            virtual handle add(const aa& a) const
            { return handle(new bb(.../*new value for aa with (bb,aa)*/)); }
            virtual handle add(const bb& a) const
            { return handle(new bb(.../*new value for bb with (bb,bb)*/)); }
        };
    
        handle() :ps() {}
        //support both copy (by clone) and move (by stole)
        handle(const handle& s) :ps(s.ps? s.ps->clone(): nullptr) {}
        handle(handle&& s) :ps(s.ps) { s.ps=nullptr; };
        //assign by value, to force temporary assign
        handle& operator=(handle h) { delete ps; ps=h.ps; h.ps=0; return *this; }
        //cleanup
        ~handle() { delete ps; }
    
        //the operator+
        friend handle operator+(const handle& a, const handle& b)
        { 
            return (b.ps && a.ps)? b.ps->add_invoke(*a.ps): handle(); 
            //Note: manage also the a+b with one of `a` or `b` as null, if it make sense
        }
    
    private:
        handle(root* p) :ps(p) {}
    
        root* ps;
    };