将shared_ptr <Base>传递为shared_ptr <Derived>

Mar*_*sov 0 c++ polymorphism inheritance double-dispatch shared-ptr

我目前有以下结构

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

}
Run Code Online (Sandbox Code Playgroud)

我收到一个错误消息,说参数列表与重载函数不匹配。在尝试传递shared_ptr<A>期望值时,shared_ptr<B>还是很有道理的shared_ptr<C>,但是如何解决这个问题呢?还有另一种方法吗?

Chr*_*phe 5

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

首先,您可以使用共享的基础指针来完美地实现多态。以下是一个小片段,向您展示如何实现:

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();  } 
};
Run Code Online (Sandbox Code Playgroud)

您的双循环将如下所示:

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 .
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您看到了,为了掌握这些组合,我使用了一个重写,该重写知道其自己对象的真实类型,但是不知道有关伙伴对象的真实类型的任何具体信息。因此,要弄清楚哪种配对,我需要调用伙伴对象的多态函数。

在线演示

解决问题的更通用方法是双重调度

这个小的概念证明只是一个简单的例子。可以将问题分解为一部分问题的每个伙伴对象中的理想选择。但是事情并不总是那么简单,因此您可以通过谷歌搜索双重调度找到更多复杂的技术。幸运的是,碰撞的例子很常见。

这是另一个结合了重载和重载的演示。我认为这是您尝试实现的目标,但是可以通过一个间接的层次来解决。它受到访客模式的启发:使用指向伙伴对象基类的共享指针来调用对象的多态碰撞函数。但是,此函数的实现立即调用伙伴对象的多态函数,并将其作为对自身的引用作为参数(即,对参数实型的了解,允许编译器选择正确的重载)。不幸的是,这种“反弹”方法(顺便说一句,它是一种反向访问者)要求基类知道所有可能的派生类,这远非理想。但是它允许为每种可能的组合提供不同的行为。

双重调度的另一种方法是使用调度表。通过管理一种虚拟表但具有两种类型的虚拟表,并进行一些查找以调用正确的功能以实现正确的组合,可以工作。