传递shared_ptr as shared_ptr

问题描述 投票:0回答:1

我目前有以下结构

class A

class B : public A

class C : public A

我有在AB中定义的虚拟方法,而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<A>我期待shared_ptr<B>shared_ptr<C>,但我该怎么解决这个问题?还有另一种方法吗?

c++ inheritance polymorphism shared-ptr double-dispatch
1个回答
5
投票

Let's make it work with virtual functions and shared pointer to base class

首先,您可以使用到基础的共享指针完美地使多态性工作。这里有一个小片段,向您展示如何做到这一点:

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

The more general approach to your problem is double dispatch

这个小概念证明就是一个简单的例子。当问题可以在每个伙伴对象中分解问题的一部分时,它是理想的。但事情并不总是那么简单,所以你可以通过Google搜索double dispatch找到更精细的技巧。幸运的是,碰撞的例子非常普遍。

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

双重调度的另一种方法是使用dispatch table。这可以通过管理一种虚拟表来实现,但有两种类型,并且有一些查找可以为正确的组合调用正确的函数。

© www.soinside.com 2019 - 2024. All rights reserved.