简体   繁体   English

C++,一种在矢量调整大小后更新指针并在不复制的情况下擦除矢量对象的方法?

[英]C++, A way to update Pointers after vector resize, and erase vector objects without copying?

I believe this will be my first question for the site, so I apologize for any mistakes or errors in this post.我相信这将是我对该网站的第一个问题,因此对于本文中的任何错误或错误,我深表歉意。 I am a beginner C++ programmer as well, so forgive me if my questions come across as “noobish”.我也是一个初学者 C++ 程序员,所以如果我的问题遇到“noobish”,请原谅我。

Background: A collection of Parent Entity objects are created at startup (and currently not removed or added-to during runtime), and are then linked to a series of Activator Entity objects (both at the beginning, and during, runtime) through a Child Entity object.背景:在启动时创建父实体对象的集合(当前在运行时未删除或添加),然后通过子链接到一系列激活器实体对象(在开始时和运行时)实体 object。 When establishing a link, the Parent generates a Child (which is stored in a local vector), and returns a pointer to the Child for the Activator to store.建立链接时,Parent 生成一个 Child(存储在本地向量中),并返回指向 Child 的指针供 Activator 存储。

Activators will “activate” children they are linked with, which will then do jobs based off internal and Parent settings.激活器将“激活”与它们链接的子级,然后它们将根据内部和父级设置执行工作。 After being activated, they are also updated periodically by the Parent, continuing until eventually deactivating.激活后,它们也会由父级定期更新,一直持续到最终停用。

Below is a simplified example of the classes present.下面是现有类的简化示例。

class ParentEntity {
    std::vector<ChildEntity> m_Children;
    std::vector<ChildEntity*> m_ActiveChildren;
public:
    //Funcs
    ParentEntity(unsigned expectedChildren) { m_Children.reserve(expectedChildren); }
    ChildEntity* AddChild(){
        m_Children.push_back(ChildEntity(*this));
        return &(m_Children.back());
    }
    void RemoveChild(unsigned iterator) {
        //Can't figure a way to remove from the m_Children list without disrupting all pointers. 
        //m_Children.erase(m_Children.begin() + iterator); Uses Copy operators, which wont work as Const values will be present in Child
    }
    void AddActiveChild(ChildEntity* activeChild) {
        m_ActiveChildren.push_back(activeChild);
    }
    bool Update(){  //Checks if Children are active, 
        if (!m_ActiveChildren.empty()) {
            std::vector<ChildEntity*> TempActive;
            TempActive.reserve(m_ActiveChildren.size());
            for (unsigned i = 0; i < m_ActiveChildren.size(); i++) {
                if (m_ActiveChildren[i]->Update()) {
                    TempActive.push_back(m_ActiveChildren[i]);
                }
            }
            if (!TempActive.empty()) {
                m_ActiveChildren = TempActive;
                return true;
            }
            else {
                m_ActiveChildren.clear();
                return false;
            }
        }
        else {
            return false;
        }
    }
};

class ChildEntity {
public:
    ChildEntity(ParentEntity& Origin) //Not const because it will call Origin functions that alter the parent
        :
        m_Origin(Origin)
    {}
    void SetActive() {
        m_ChildActive = true;
        m_Origin.AddActiveChild(this);
    }
    bool Update() { //Psuedo job which causes state switch
        srand(unsigned(time(NULL)));
        if ((rand() % 10 + 1) > 5) {
            m_ChildActive = false;
        }
        return m_ChildActive;
    }
private:
    ParentEntity& m_Origin;
    bool m_ChildActive = false;
};


class ActivatorEntity {
    std::vector<ChildEntity*> ActivationTargets;
public:
    ActivatorEntity(unsigned expectedTargets) { ActivationTargets.reserve(expectedTargets); }
    void AddTarget(ParentEntity& Target) {
        ActivationTargets.push_back(Target.AddChild());
    }
    void RemoveTarget(unsigned iterator) {
        ActivationTargets.erase(ActivationTargets.begin() + iterator);
    }
    void Activate(){
        for (unsigned i = 0; i < ActivationTargets.size(); i++) {
            ActivationTargets[i]->SetActive();
        }
    }
};

With that all laid out, my three questions are:综上所述,我的三个问题是:

  1. Is there a way to update Pointers when a vector resizes?当向量调整大小时,有没有办法更新指针?

When a Child is added, if it goes past the expected capacity, the vector creates a new array and moves the original objects to the new location.添加 Child 时,如果它超出预期容量,则向量会创建一个新数组并将原始对象移动到新位置。 This breaks all of the Activator pointers, and any m_ActiveChild pointers, as they are pointing to the old location.这会破坏所有 Activator 指针和任何 m_ActiveChild 指针,因为它们指向旧位置。

  1. Is there a way to remove Child objects from the m_Children vector?有没有办法从 m_Children 向量中删除子对象?

Since ChildEntity objects will host const items within them, copy assignment operations won't work smoothly, and the Vector's erase function won't work.由于 ChildEntity 对象将在其中托管 const 项,因此复制分配操作将无法顺利进行,并且 Vector 的擦除 function 将无法正常工作。 The m_Children vector could be rebuilt without the unwanted object through a temporary vector and copy constructor, but this leads to all of the pointers being wrong again. m_Children 向量可以通过临时向量和复制构造函数在没有不需要的 object 的情况下重建,但这会导致所有指针再次出错。

  1. Please let me know if there are any other suggested optimizations or corrections I should make!如果有任何其他建议的优化或更正,请告诉我!

Thank you all for your help!谢谢大家的帮助!

Your problem, abstractly seen, is that on one hand you have collections of objects that you want to iterate through, kept in a container;抽象地看,您的问题是,一方面您有 collections 的要迭代的对象,并保存在容器中; and that on the other hand these objects are linked to each other.另一方面,这些对象是相互关联的。 Re-ordering the container destroys the links.重新排序容器会破坏链接。

Any problem can be solved by an additional indirection: Putting not the objects but object handles in the container would make re-ordering possible without affecting cross-references.任何问题都可以通过额外的间接方式解决:不将对象而是object 句柄放入容器中,可以在不影响交叉引用的情况下重新排序。 The trivial case would be to simply use pointers;最简单的情况是简单地使用指针; modern C++ would use smart pointers.现代 C++ 将使用智能指针。

The disadvantage here is that you'll move to dynamic allocation which usually destroys locality right away (though potentially not if most allocations happen during initialization) and carries the usual run-time overhead.这里的缺点是您将转向动态分配,这通常会立即破坏局部性(尽管如果大多数分配发生在初始化期间,则可能不会)并带有通常的运行时开销。 The latter may be prohibitive for simple, short-lived objects.后者对于简单的、短暂的对象可能是禁止的。

The advantage is that handling pointers enables you to make your objects polymorphic which is a good thing for "activators" and collections of "children" performing "updates": What you have here is the description of an interface which is typically implemented by various concrete classes.优点是处理指针使您能够使您的对象多态,这对于“激活器”和执行“更新”的“孩子”的 collections 来说是一件好事:您在这里拥有的是对通常由各种具体实现的接口的描述类。 Putting objects in a container instead of pointers prevents such a design because all objects in a container must have the same concrete type.对象而不是指针放入容器中会阻止这种设计,因为容器中的所有对象都必须具有相同的具体类型。

If you need to store more information you can write your own handle class encapsulating a smart pointer;如果您需要存储更多信息,您可以编写自己的句柄 class 封装智能指针; perhaps that's a good idea from the beginning because it is easily extensible without affecting all client code with only a moderate overhead (both in development and run time).也许从一开始这就是一个好主意,因为它很容易扩展,而不会影响所有客户端代码,而且开销适中(在开发和运行时)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM