Как можно кратко сравнить значения и вызвать функции базового класса многих производных классов?

#c #iteration #sfml #derived-class #base-class

#c #итерация #sfml #производный класс #базовый класс

Вопрос:

У меня есть 2d-физический движок, который я программировал на C с использованием SFML; Я реализовал грубую систему обнаружения столкновений для всех SandboxObject s (базовый класс для каждого типа физического объекта), но у меня дилемма.

Я планирую иметь много разных производных классов SandboxObject s, таких как Circle s, Rect s и так далее, Но мне нужен способ проверить roughHitbox SandboxObject , сталкивается ли каждый из них с другим.

Когда программа запускается, она выделяет память, скажем, на 10 000 Circles

 int circleCount = 0;//the number of active Circles
constexpr int m_maxNumberOfCircles = 10000;//the greatest number of circles able to be set active
Circle* m_circles = new Circle[m_maxNumberOfCircles];//create an array of circles that aren't active by default
 

вот так.

и каждый раз, когда пользователь «порождает» новый Circle , выполняется код

 (m_circles   circleCount)->setActive();`
circleCount  
 

Circle s, которые не являются активными, по сути, вообще не существуют; у них могут быть позиции и радиусы, но эта информация никогда не будет использоваться, если она Circle не активна.

Учитывая все это, то, что я хочу сделать, это перебрать все различные массивы производных классов SandboxObject because SandboxObject — это базовый класс, который реализует грубый материал hitbox , но поскольку будет много разных производных классов, я не знаю лучшего способа сделать это.

Один из подходов, который я попробовал (с небольшим успехом), заключался в том, чтобы иметь указатель на SandboxObject

 SandboxObject* m_primaryObjectPointer = nullptr;
 

этот указатель был бы нулевым, если бы не было> 1 SandboxObject с активным; с его помощью я попытался использовать функции увеличения и уменьшения, которые проверяли, может ли он указывать на следующий SandboxObject , но я не мог заставить это работать должным образом, потому что указатель базового класса на производный класс действует странно. :/

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

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

Комментарии:

1. Два (несвязанных) момента: у вас нет явной обработки памяти. Для контейнера объектов используйте std::vector . Во-вторых, (m_circles circleCount)->setActive(); точно равно m_circles[circleCount].setActive(); . Последнее обычно легче читать и понимать.

2. Больше связано с вашей проблемой, является ли класс полиморфным? Тогда почему бы не использовать один std::vector<std::unique_ptr<SandboxObjects>> для всех активных объектов? Тогда вам не нужно отслеживать какие-либо «неактивные» объекты, поскольку они даже не будут существовать. И поскольку у вас есть один контейнер для всех «активных» объектов, вы можете более легко перебирать их по мере необходимости.

3. Рассматривали ли вы статический вектор в классе SandboxObject, который содержит указатели на все созданные объекты. В вашем конструкторе SandboxObject у вас может быть m_all_objects.push_back(this); Деструктор должен будет установить указатель на nullptr , который вы затем можете проверить.

4. Вы можете найти лучший пул опыта в gamedev stack exchange gamedev.stackexchange.com .

Ответ №1:

Ваши проблемы вызваны вашим желанием использовать полиморфный подход к неполиморфным контейнерам.

Преимущество a SandboxObject* m_primaryObjectPointer в том, что оно позволяет вам обрабатывать ваши объекты полиморфно: m_primaryObjectPointer -> roughtHitBox() будет работать независимо от реального типа объекта Circle , Rectangle , или a Decagon .

Но итерация с использованием m_primaryObjectPointer не будет работать так, как вы ожидаете: эта итерация предполагает, что вы выполняете итерацию по смежным объектам в массиве SandboxObject элементов (т. Е. Компилятор будет использовать расположение памяти базового типа для вычисления следующего адреса).

Вместо этого вы можете рассмотреть возможность итерации по вектору (или массиву, если вы действительно хотите иметь дело с дополнительными проблемами управления памятью) указателей.

 vector<SandboxObject*> universe;  
populate(universe); 
for (auto object:unviverse) {
    if (object->isActive()) {
        auto hb = object -> roughtHitBox(); 
        // do something with that hitbox
    }
}
 

Теперь управление объектами во вселенной также может быть болезненным. Поэтому вы можете рассмотреть возможность использования интеллектуальных указателей вместо этого:

 vector<shared_ptr<SandboxObject>> universe;  
 

(небольшая демонстрация)

Комментарии:

1. «Но итерация с использованием m_primaryObjectPointer не будет работать так, как вы ожидаете» да, я обнаружил это на собственном горьком опыте.

Ответ №2:

Трудно ответить на этот вопрос, не зная требований, но вы могли бы sandbox поддерживать два вектора активных и неактивных объектов и использовать unique_ptrs базового класса для управления памятью.

Некоторый код ниже:

 #include <vector>
#include <memory>
#include <iostream>

class sandbox_object {
public:
    virtual void do_something() = 0;
};

class circle : public sandbox_object {
private:
    float x_, y_, radius_;
public:
    circle(float x, float y, float r) :
        x_(x), y_(y), radius_(r)
    {}

    void do_something() override {
        std::cout << "i'm a circle.n";
    }
};

class triangle : public sandbox_object {
private:
    float x1_, y1_, x2_, y2_, x3_, y3_;
public:
    triangle( float x1, float y1, float x2, float y2, float x3, float y3) :
        x1_(x1), y1_(y1), x2_(x2), y2_(y2), x3_(x3), y3_(y3)
    {}

    void do_something() override {
        std::cout << "i'm a triangle.n";
    }
};

class sandbox {
    using sandbox_iterator = std::vector<std::unique_ptr<sandbox_object>>::iterator;
private:
    std::vector<std::unique_ptr<sandbox_object>> active_objects_;
    std::vector<std::unique_ptr<sandbox_object>> inactive_objects_;
public:
    void insert_circle(float x, float y, float r) {
        active_objects_.push_back( std::make_unique<circle>(x, y, r) );
    }

    void insert_triangle(float x1, float y1, float x2, float y2, float x3, float y3) {
        active_objects_.push_back( std::make_unique<triangle>(x1,y1,x2,y2,x3,y3));
    }

    sandbox_iterator active_objs_begin() {
        return active_objects_.begin();
    }

    sandbox_iterator active_objs_end() {
        return active_objects_.end();
    }

    void make_inactive(sandbox_iterator iter) {
        std::unique_ptr<sandbox_object> obj = std::move(*iter);
        active_objects_.erase(iter);
        inactive_objects_.push_back(std::move(obj));
    }

};

int main() {
    sandbox sb;

    sb.insert_circle(10.0f, 10.0f, 2.0f);
    sb.insert_triangle(1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 1.0f);
    sb.insert_circle(1.0f, 6.0f, 4.0f);

    sb.make_inactive(sb.active_objs_begin());
    (*sb.active_objs_begin())->do_something(); // this should be the triangle...

    return 0;
}