C ++重载虚拟+运算(c++ overloading virtual + operator)

2019-09-16 14:29发布

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

输出: 2;2 ,然后将其在崩溃线b3 = dynamic_cast<bb&>(a4); 给错误std::bad_cast at memory location 0x0012fdbc..

我发现原因是A2 + A3的表达的结果来为AA类的对象,并在接下来的发言中,我们试图将它转换为派生类对象,它是无效的,因此导致异常。 所以我的查询就可以实现上述意图,即aa &a4 = a2 + a3; 在上述功能的一些变化?

Answer 1:

C ++有一个单一的调度(基于虚拟函数)。 多晶型二元操作不可避免地陷入“双调度”的情况下,并且因此不能由只是简单的虚拟功能的手段polymorphicaly实现。

此外,因为你是返回一个值,每个子类的信息丢失。

处理这种情况更正确的方法是定义一个实现业务和持有多态类型,委托给他们的操作执行的非多态性手柄。

喜欢

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


Answer 2:

字面上重载aa operator +(aa& obj)在派生类没有什么意义。

即使当虚拟现在你会调用返回的方法aa在任何时间,所以结果a2 + a3将是aa的对象,从来没有一个BB的对象。 它被构造为bb对象,但复制到所产生的aa对象,副本失去bb信息。

你应该做的是替代特定版本的bb operator +(const bb& obj)注意,然而,这将隐藏原始operator+aa论据,你仍然有重新定义它或进口的using语句 。

现在,这些是更好的语义,但它不会解决你的问题,因为在你的情况下,它仍然会调用operator+返回一个aa对象。 在这种情况下,如果你真的想解决这个问题,你应该下来投a2a3他们面前

但实际上,如果你在虚拟化功能的父子系统做这样的事情,你可能会重新考虑你的设计。 通常情况下,使用的dynamic_cast应该提高在任何情况下,一些人感到惊讶。 这当然有它的用途,但再次向下转换应避免尽可能。



Answer 3:

你传递一个基类,它为什么会转换为派生投? 你应该抓住从铸造例外,因为这是你如何知道类型是不是你所期望的。

需要注意的是,虽然a1a2都参考的情况下bba4是没有的。 这就是为什么前两个类型转换没有抛出异常。



文章来源: c++ overloading virtual + operator