【问题标题】:storing a reference/pointer to a std::set object存储指向 std::set 对象的引用/指针
【发布时间】:2012-11-21 06:47:34
【问题描述】:

我正在制作一个关于弹珠的游戏,它使用一个结构来描述弹珠的属性:

struct Marble
{
Color color;
int weight;
//Other customizable marble properties
};

我目前正在使用std::vector 来跟踪哪些弹珠在游戏板上的哪个位置:

std::vector<Marble> m_marbles;

在任何时候,我都可以通过索引向量来检查给定位置的弹珠的属性。检查位置 20 的弹珠颜色:

m_marbles[20].color;

用户可以创建自定义弹珠并将它们添加到游戏板上。如果我想在游戏板上添加一个新的弹珠,我会创建一个新的结构对象并将其推送到向量上:

Marble m;

//Fill struct with marble information

m_marbles.push_back(m)

问题是游戏板非常大(成千上万的弹珠),而且许多弹珠完全相同。

假设游戏板上有 4000 个位置。也许其中 3000 个是重量为 1.0 的红色弹珠。在当前的解决方案中,3000 个等效的 Marble 对象占用了我的向量中的空间。

由于用户可以定义自己的弹珠类型,我不能只硬编码所有可能的弹珠类型,然后让向量存储指向适当弹珠对象的指针。

我正在考虑使用std::set 来存储不同的大理石类型。然后,当用户去创建他的自定义弹珠时,他可以尝试将其插入到集合中。如果同类型的弹珠已经存在,就不会浪费内存了。

我的问题是,如何将引用存储到 std::set 中?例如,假设我的集合由以下大理石类型组成:

{RedMarble, BlueMarble, StrangeMarble, ConfusedMarble}

现在我想将一个红色弹珠分配到游戏板上的 20 位:

m_marbles[20] = ?

我还想检查31位大理石的颜色:

m_marble[31].color;

是否可以以这种方式使用集合? find 和 insert 函数返回迭代器,但如果我在集合中插入更多项,这些迭代器不会失效吗?

【问题讨论】:

  • 在我看来你不想要一套。你可能想要一个向量,甚至只是一个数组。
  • 您更关心内存占用还是性能?选择其中之一将确定最适合您的解决方案。
  • @Dave - Marble* 的向量?如果没有集合,我如何确定每种不同类型的大理石中只有一种存在(并且正在占用内存)?
  • @Chad - 性能是个问题,因为游戏板是可扩展的。想象一下,当其中 150,000 个包含相同数据时,拥有 300,000 个 Marble 对象。使用 Marble* 的向量的问题是,如果 Marble 的类型已经存在,我需要一种方法来获取指向它的指针。如果它不存在,我可以new 一个新的 Marble 对象并将其添加到唯一 Marble 类型列表中。
  • Quadtrees(和其他空间索引)是相切的,但如果您实际上有 300K 大理石场景并且想要测试碰撞等,您可能会感兴趣......漂亮的网络演示:lab.polygonal.de/?p=202

标签: c++


【解决方案1】:

是的,你可以做你想做的事。您可以将指针存储在向量中,这些指针指向集合中的弹珠。或者你可以将迭代器存储在你的向量中,迭代器再次指向你集合中的 Marbles。

这是我将如何使用指针:

// A useful helper funtction -- not required, but useful.
const Marble* Helper(const Marble& m) {
  static std::set<Marble> uniqueMarbles;
  return &*(uniqueMarbles.insert(m).first);
}

// A vector of pointers to marbles
std::vector<const Marble*> m_marbles;

// You can pass temporaries to the helper function, and it returns a permanent pointer
m_marbles.push_back(Helper(Marble(RED, 1.0));

Marble m;
m.setColor(BLUE);
m_marbles.push_back(Helper(m));


// You can reference your marbles
Color c = m_marbles[i]->getColor();

// Hopefully the following will produce a compiler error message
m_marbles[i]->setColor(GREEN); // Oops, m_marbles holds const pointers

【讨论】:

  • std::set 中元素的地址是否保证保持不变?我想也许随着集合的增长,它可能不得不重新分配类似于向量的内存。
  • 问是因为我想知道是否可以将 &(*first) 的地址存储在我的向量中,而不必每次都查找元素。
  • std::set 的元素地址保证保持不变,直到您删除该项目或销毁该集合。
【解决方案2】:

您应该使向量包含指向 Marble 结构的 指针

std::vector<Marble*> m_marbles;

指针不会占用太多空间,您仍然可以使用Marble 作为基类。

【讨论】:

  • Marble* 的向量是理想的,但问题是复制相同类型的大理石。当用户去创建一个新的弹珠时,我不能只通过new 创建一个新弹珠并存储一个指向它的指针。我需要检查大理石的类型是否不存在。如果该类型的大理石不存在,我可以创建一个新的 Marble 对象并存储一个指向该对象的指针。如果它已经存在,我只想存储一个指向现有 Marble 对象的指针。我认为std::set 是理想的,因为它不允许重复对象。
【解决方案3】:

如果游戏板真的只有几千块大,我不会在意浪费空间。例如

10000 places * sizeof(Marble) = 10000 * 100 bytes ~ 1 MB

按照今天的标准,内存并不大。

如果它可能变得非常大,那么您确实可以将您独特的 Marbles 存储在一个集合中并使用指针引用它们,正如 @BenRuijl 已经展示的那样。

【讨论】:

  • 嗯,游戏板是可扩展的,因此它可以变得非常大。随着越来越多的弹珠添加到游戏板上,它会无缘无故地占用越来越多的内存,因为结构包含相同的数据。
【解决方案4】:

构建一个类Marble,其中包含一个指向MarbleType 的指针,该指针定义了每个弹珠的属性(对于每种弹珠,您都有一个MarbleType 的实例)。该向量可能包含Marbles 或指向Marbles 的共享指针或唯一指针。如果Marble 类很小(享元模式),您不会浪费空间,但仍然有一个干净灵活的解决方案。可以命名弹珠类型(例如通过字符串)并存储在 std::map 中。

【讨论】:

    猜你喜欢
    • 2021-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 2021-05-20
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多