2
#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;对上述功能进行一些更改?

4

3 回答 3

3

C++ 有一个分派(基于虚函数)。多态二元运算符不可避免地陷入“双重调度”的情况,因此不能仅通过简单的虚函数来实现多态。

此外,由于您要返回一个值,因此每个子类信息都会丢失。

处理这种情况的更合适的方法是定义一个非多态句柄来实现操作并保存多态类型,将操作执行委托给它们。

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;
};
于 2012-04-17T07:53:03.657 回答
2

派生类中的字面重载aa operator +(aa& obj)几乎没有意义。

即使是虚拟的,你现在也会调用一个aa随时返回的方法,所以结果a2 + a3将是一个aa对象,而不是一个 bb 对象。它被构造为一个bb对象,但被复制到生成的aa对象中,并且副本丢失了bb信息。

你应该做的是覆盖一个特定的版本bb operator +(const bb& obj),注意这将隐藏原来operator+aa参数,所以你仍然需要重新定义它或使用语句using导入

现在,这些是更好的语义,但它不会解决您的问题,因为在您的情况下,它仍然会调用operator+返回aa对象的 。在这种情况下,如果你真的想解决这个问题,你应该a2在添加它们a3之前向下转换。

但实际上,如果您在具有虚拟化功能的父子系统中执行此类操作,您可能会重新考虑您的设计。通常,dynamic_cast在任何情况下使用 a 都应该引起一些注意。它当然有它的用途,但应尽可能避免再次向下转换。

于 2012-04-17T06:52:17.733 回答
0

您正在传递一个基类,为什么要将它转换为派生转换?您应该从强制转换中捕获异常,因为这就是您知道类型不是您期望的类型的方式。

请注意,whilea1和被引用到,a2的实例不是。这就是前两次强制转换没有抛出异常的原因。bba4

于 2012-04-17T06:26:20.400 回答