如何防止在多个向量中添加对象?

时间:2019-05-01 06:25:37

标签: c++ algorithm design-patterns vector data-structures

有些对象是Drawable,有些是Movable
所有可移动对象都是可操作的。
我将所有可绘制对象存储在向量drawables中,将可移动对象存储在向量movables中。 我也有向量shipsbullets,它们分别包含类型为ShipBullet的对象。 ShipBullet均为Movable

这是类的结构:

class Drawable {
public:
    void draw();
};

class Movable : public Drawable {
public:
    void move();
}

class Ship : public Movable {
public:
    Ship();
}

class Bullet : public Movable {
public:
    Bullet();
}

向量声明如下:

std::vector<Drawable*> drawables;
std::vector<Movable*> movables;
std::vector<Ship*> ships;
std::vector<Bullet*> bullets;

问题是,每次创建飞船时,都必须将其添加到所有向量中,即

drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);

我创建了单独的drawablesmovables向量,因为我有一个draw()函数,该函数调用draw()向量中所有对象的drawables方法。同样,我有一个move()函数,该函数调用move()向量中所有对象的movables方法。

我的问题是,如何更改结构以防止在不同向量中添加相同的事物。完成目标后,我还需要从所有矢量中删除对象。
例如,一旦子弹击中某人或移出屏幕,那么我必须在所有三个向量中对其进行搜索之后,将其从向量drawablesmovablesbullets中删除。
似乎我没有使用正确的方法来存储这些对象。请提出一个替代方案。

这似乎更像是软件工程问题,而不是编码问题。如有必要,请将问题迁移到其他论坛。

3 个答案:

答案 0 :(得分:1)

假设您使用的是相当现代的编译器,这就是shared_ptr存在的原因。

问题在于您不知道哪个向量拥有该对象,因此您不知道要删除哪个对象。 shared_ptr可以帮您解决这个问题:它可以管理对象的生存期,并在销毁对对象的最后一个引用后将其删除。

要创建新的Ship,可以执行以下操作:

auto ship = std::make_shared<Ship>();

drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);

在这一点上,ship有4个引用(每个向量一个,ship变量本身)。一旦将其从所有三个向量中删除,并且本地变量超出范围,它将自动删除。

答案 1 :(得分:0)

由于所有MovableShipBullet都是Drawable,为什么不只制作一个Drawable项的向量,然后将所有里面的不同类?您将只有一个指向所有不同实例的指针,而您将只有这些指针的一个向量。但是,此解决方案可能需要将Drawable设为抽象类。

答案 2 :(得分:0)

如果要维护包含(指向)某种类型的所有对象的容器,则可能需要采用RAII方法。将对象的构造函数添加到容器,然后将析构函数从容器中删除。您还希望确保没有其他修改容器,因此它应该是类的私有(静态)成员,并使用公共方法提供只读访问权限。

更好的是,将此逻辑移到其自己的类中,以便可以重新使用它。这也将使您现有的容器可以专注于当前的工作。他们只需要一个新的helper类类型的数据成员即可。

为了简化删除操作,我考虑使用list代替vector。另外,可能值得使用reference_wrapper代替指针。指针可以具有空值。尽管您可以证明容器将没有空指针,但是reference_wrapper可以在没有其他文件的情况下传达这一信息。

为了让您入门,这是您可以使用的帮助程序类模板的开始。

template <class T>
class All {
        using ListType = std::list< std::reference_wrapper<T> >;

    private:
        static ListType the_list;
        // A list's iterators are rarely invalidated. For a vector, you would
        // not store an iterator but instead search when removing from the_list.
        typename ListType::iterator list_it;

    public:

        // Read-only access to the list.
        static const ListType & list() { return the_list; }

        // Construction
        ListAll() : list_it(the_list.end()) {}  // If this constructor is needed
        explicit ListAll(T & data) : list_it(the_list.insert(the_list.end(), data)) {}

        // Destruction
        ~ListAll() { if ( list_it != the_list.end() ) the_list.erase(list_it); }

        // Rule of 5
        // You should also define or delete the copy constructor, move constructor,
        // copy assignment, and move assignment.

        // If you need the default constructor, then you probably want a method like:
        //void set(T & data);
};
template <class T>
typename All<T>::ListType All<T>::the_list{};

名称通常很难获得。我根据要迭代的内容来命名此模板,例如:All<Movable>::list()