如何简洁地比较许多派生类的基类的值并调用它们的函数?

Posted

技术标签:

【中文标题】如何简洁地比较许多派生类的基类的值并调用它们的函数?【英文标题】:How would one succinctly compare the values of and call the functions of many derived classes' base class? 【发布时间】:2022-01-14 13:16:30 【问题描述】:

我有一个二维物理引擎,我一直在使用 SFML 用 C++ 进行编程;我已经为所有SandboxObjects(每种物理对象的基类)实现了一个粗略的碰撞检测系统,但我有一个两难选择。

我计划有许多不同的SandboxObjects 派生类,例如Circles、Rects 等等,但我想要一种方法来检查每个SandboxObjectroughHitbox与另一个相撞。

当程序启动时,它会为 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++

不存在的Circles 根本不存在;它们可能具有位置和半径,但如果 Circle 未激活,则该信息将永远不会被使用。

鉴于这一切,我想做的是循环 all SandboxObject 的派生类的不同数组,因为 SandboxObject 是实现粗略的 hitbox 东西的基类,但是因为会有很多不同的派生类,我不知道最好的方法。

我尝试过的一种方法(收效甚微)是使用指向 SandboxObject 的指针

SandboxObject* m_primaryObjectPointer = nullptr;

除非有 > 1 个SandboxObjects 处于活动状态,否则此指针将为空;有了它,我尝试使用递增和递减函数来检查它是否可以指向下一个SandboxObject,但我无法让它正常工作,因为指向派生类的基类指针行为很时髦。 :/

我不是在寻找确切的代码实现,只是一种用于处理许多不同派生类的基类的经过验证的方法。

让我知道我是否应该在这个问题中编辑任何内容,或者我是否可以提供更多信息。

【问题讨论】:

两点(不相关的):不要自己进行显式内存处理。对于对象容器,请使用 std::vector。其次,(m_circles + circleCount)->setActive(); 正好等于m_circles[circleCount].setActive();。后者通常更容易阅读和理解。 与您的问题更相关,类是多态的吗?那么为什么不对所有 active 对象使用一个 std::vector<std::unique_ptr<SandboxObjects>> 呢?然后您不必跟踪任何“非活动”对象,因为它们甚至都不存在。而且由于您为所有“活动”对象提供了一个容器,因此您可以根据需要更轻松地迭代它们。 您是否考虑过 SandboxObject 类中的静态向量,它包含指向所有已创建对象的指针。在您的 SandboxObject 构造函数中,您可以拥有 m_all_objects.push_back(this);析构函数必须将指针设置为 nullptr,然后您可以检查它。 您可能会在 gamedev 堆栈交换 gamedev.stackexchange.com 中找到更好的经验池。 【参考方案1】:

您的问题是由于您希望在非多态容器上使用多态方法造成的。

SandboxObject* m_primaryObjectPointer 的优势在于它允许您以多态方式处理对象:无论对象的实际类型是 CircleRectangle 还是 Decagonm_primaryObjectPointer -> roughtHitBox() 都可以工作。

但是使用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;  

(小demo)

【讨论】:

“但是使用 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;

【讨论】:

以上是关于如何简洁地比较许多派生类的基类的值并调用它们的函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在另一个类的向量中调用派生类的析构函数

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???

详解C++中基类与派生类的转换以及虚基类

生成一个派生类对象时,调用基类和派生类构造函数按啥次序

如何初始化作为另一个类的成员变量的基类对象?

如何在没有虚方法的情况下创建派生类的基类?