Передача shared_ptr <Base>как shared_ptr <Derived> - PullRequest
0 голосов
/ 03 марта 2019

В настоящее время у меня есть следующая структура

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<A>, где я ожидаю shared_ptr<B> или shared_ptr<C>, но как мне обойти эту проблему?Есть ли другой способ сделать это?

1 Ответ

0 голосов
/ 03 марта 2019

Давайте заставим его работать с виртуальными функциями и совместно используемым указателем на базовый класс

Прежде всего, вы можете прекрасно заставить полиморфизм работать, используя общие указатели на базу.Вот небольшой фрагмент, чтобы показать вам, как вы можете это сделать:

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 .
    }
}

Теперь, видите, чтобы освоить комбинацииЯ использовал переопределение, которое знает реальный тип своего собственного объекта, но не знает ничего конкретного о реальном типе объекта-партнера.Итак, чтобы выяснить, какой это тип спаривания, мне нужно вызвать полиморфную функцию объекта-партнера.

Демонстрация в Интернете

Более общий подход к вашей проблеме - двойная рассылка

Это небольшое доказательство концепции - показать простой пример.Идеально, когда проблема может быть разложена в каждом объекте-партнере, выполняющем одну часть задачи.Но все не всегда так просто, поэтому вы можете найти более сложные приемы, прибегая к помощи для 1011 * двойной отправки К счастью, пример столкновения довольно распространен.

Здесь еще одна демонстрация , в которой используется комбинация переопределения и перегрузки.Я думаю, что это то, чего вы пытаетесь достичь, но решает это еще на один уровень косвенности.Она основана на шаблоне посетителя : функция полиморфного столкновения объекта вызывается с общим указателем на базовый класс объекта партнера.Но реализация этой функции немедленно вызывает полиморфную функцию объекта-партнера с указанием в качестве аргумента ссылки на себя (т. Е. Знание реального типа аргумента позволяет компилятору выбрать правильную перегрузку).К сожалению, такой подход «отказов» (кстати, это своего рода перевернутый посетитель) требует, чтобы базовый класс знал весь свой потенциальный производный класс, что далеко от идеала.Но это позволяет предложить различное поведение для каждой возможной комбинации.

Другим подходом к двойной диспетчеризации является использование таблицы диспетчеризации .Это работает путем управления своего рода виртуальной таблицей, но с двумя типами, и некоторым поиском для вызова правильной функции для правильной комбинации.

...