代码之家  ›  专栏  ›  技术社区  ›  Martin Spasov

将shared\u ptr<Base>传递为shared\u ptr<Derived>

  •  1
  • Martin Spasov  · 技术社区  · 7 年前

    我目前有以下结构

    class A

    class B : public A

    class C : public A

    我在中定义了虚拟方法 A B C 正在覆盖它们。方法就是这样的

    bool C::CheckCollision(shared_ptr<B> box);

    bool B::CheckCollision(shared_ptr<C> triangle);

    我还有一个向量 shared_ptr<A>

    for (int i = 0; i < objects.size(); i++)
    {
        for (int j=i; j < objects.size(); j++
        {
            objects[i]->CheckCollision(objects[j]);
        }
    
    }
    

    我期待的地方 shared_ptr<B> shared_ptr<C> ,但我该如何处理这个问题呢?还有别的办法吗?

    1 回复  |  直到 7 年前
        1
  •  5
  •   Christophe    7 年前

    让我们使用虚拟函数和指向基类的共享指针

    class A {
    public: 
        virtual void show() { cout<<"A"<<endl; } 
        virtual void collide(shared_ptr<A> a) { cout<<"collide A with "; a->show();  } 
        virtual ~A() {}
    };
    
    class B : public A {
    public:
        void show() override { cout<<"B"<<endl; } 
        void collide(shared_ptr<A> a) override { cout<<"collide B with "; a->show();  } 
    };
    
    class C : public A {
    public:
        void show() override { cout<<"C"<<endl; } 
        void collide(shared_ptr<A> a) override { cout<<"collide C with "; a->show();  } 
    };
    

    你的双循环看起来像:

    vector<shared_ptr<A>> objects; 
    objects.push_back (make_shared<A>());   // populate for the sake of demo
    objects.push_back (make_shared<B>()); 
    objects.push_back (make_shared<C>()); 
    
    for (int i = 0; i < objects.size(); i++)
    {
        objects[i]->show(); 
        for (int j=i; j < objects.size(); j++)
        {
            objects[i]->collide(objects[j]);   // note that you have to use -> not .
        }
    }
    

    Online demo

    解决你的问题更一般的方法是双重分派

    这个小小的概念证明就是展示一个简单的例子。当问题可以在每个伙伴对象中分解为问题的一部分时,这是非常理想的。但事情并不总是那么简单,所以你可以通过搜索找到更复杂的技术 double dispatch

    在这里 another demo 它使用重写和重载的组合。我认为这是你试图实现的东西,但通过一个层次的间接解决它。它是 inspired by the visitor pattern :使用指向伙伴对象基类的共享指针调用对象的多态冲突函数。但是这个函数的实现会立即调用伙伴对象的多态函数,并将参数作为对自身的引用(即,了解参数的实际类型,允许编译器选择正确的重载)。不幸的是,这种“反弹”方法(顺便说一句,它是一种反向访问器)要求基类知道其所有潜在的派生类,这远远不够理想。但它允许为每个可能的组合提供不同的行为。

    双重分派的另一种方法是使用 dispatch table