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

问题描述 投票:3回答:3

有一些对象是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中删除它。 好像我没有使用正确的方法来存储这些对象。请提出替代方案。

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

c++ algorithm design-patterns vector data-structures
3个回答
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变量本身)。一旦从所有三个向量中删除它并且局部变量超出范围,它将自动删除。


0
投票

由于所有MovableShipBullet都是Drawable,你为什么不制作一个Drawable项目的矢量并添加所有不同的类?您将只有一个指向所有不同实例的指针,您将只有这些指针的一个向量。然而,这个解决方案可能需要使Drawable成为一个抽象类。


0
投票

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

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

为了减轻删除,我会考虑使用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()

© www.soinside.com 2019 - 2024. All rights reserved.