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

しかし実際には、仮想化された関数を使用して親子システムでそのようなことをしている場合は、設計を再考する可能性があります。通常、aを使用すると、dynamic_castどのような場合でも眉が上がるはずです。確かにその用途はありますが、ダウンキャストは可能な限り回避する必要があります。

于 2012-04-17T06:52:17.733 に答える
0

基本クラスを渡していますが、なぜそれを派生キャストにキャストするのでしょうか? キャストから例外をキャッチする必要があります。これにより、型が期待したものではないことがわかります。

a1whileとa2は のインスタンスを参照しているbbことに注意してくださいa4。そのため、最初の 2 つのキャストは例外をスローしませんでした。

于 2012-04-17T06:26:20.400 に答える