使用附加信息扩展 Pointer/Reference

Extend a Pointer/Reference with additional information

我正在编写一个程序,当玩家穿过迷宫时程序会生成一个迷宫。每个迷宫块可能有一个北、东、南和西邻居。我正在考虑使用 pointer/reference 存储每个邻居,但我还必须保存路径是否空闲(用于未来迷宫生成或如果邻居已经生成则玩家移动)或是否被阻塞的信息。

我想到了以下方法:

// 1. aggregate data in a new data type
struct Pathway {
    bool isFree;
    MazeTile* neighbor;
}
Pathway north = {true, nullptr}

// 2. store data paralelly
MazeTile* north = nullptr;
bool isNorthFree = true;

// 3. Use inheritance to create a blocked tile
MazeTile* south = new BlockedMazeTile();

就我个人而言,我会选择第一种方法,但我以前从未见过这样做。第三个看起来不错且易于扩展,但该解决方案受限于底层迷宫设计,不能用作此类问题的通用方法。

那么,这些解决方案中哪一个是首选 - 如果 none,该怎么做?

当然,这要视情况而定,但我会选择第一个。

它比第三个更好,因为如果需要,您可以在不重新创建 class 实例的情况下解锁迷宫图块。谁知道,明天在你的魔法土地上会发生什么,对吧?灵活一些会更好。以防万一;)

此外,如果可能,您可以将迷宫图块存储为数组,前提是您的游戏区域或多或少是方形的。那么您根本不需要任何对相邻图块的引用。只是 blocked 标志或任何表示下一个方块有入口的东西。

我也建议#1,但它并不像您想象的那样罕见,因为图式数据结构将附加数据存储到连接节点的边中。

如果你想让它变得非常紧凑,你可以做的另一件事是让这些边缘(路径)简单地将索引存储到 MazeTile 而不是指针。使用索引,您可以将辅助数据存储到高位,例如,指示路径是否被阻塞或您需要的任何内容。

编辑:

根据要求,这里更详细地介绍了基于索引的解决方案。假设您这样做:

class Pathway 
{
public:
    /// Creates a new pathway.
    Pathway();

    /// Sets the index of the neighboring maze tile/node.
    void set_maze_tile(int new_index);

    /// @return The index to the neighboring maze tile/node.
    int maze_tile() const;

    /// Sets whether the pathway is free or not.
    void set_free(bool val);

    /// @return True if the pathway is free.
    bool is_free() const;

private:
    int index;
};

要做到这一点,您需要将这些迷宫图块分配到一些连续的内存块中(例如:使用 std::vector 或其他某种动态数组——它可以在不使索引无效的情况下增长)。

现在你可以这样做:

enum
{
    free_bit = 1 << (sizeof(int)*8 - 1),
    index_mask = ~free_bit
};

Pathway::Pathway(): index(0)
{
}

void PathWay::set_maze_tile(int new_index)
{
     const bool was_free = is_free();
     index = new_index;
     set_free(was_free);
}

int PathWay::maze_tile() const
{
     return index & index_mask;
}

void PathWay::set_free(bool val)
{
    if (val)
        index |= free_bit;
    else
        index &= ~free_bit;
}

bool PathWay::is_free() const
{
     return (index & free_bit) != 0;
}

有点难看,低级风格的代码,这个界面是一种无聊的 getter/setter 设计,但它会压缩你的路径,使其在 64 位上通常占用大约 16 个字节(会计对于通常的结构 padding/alignment) 到只有 4 个字节,这不仅有助于减少内存使用,而且还可以提高速度(更大的空间局部性,适合高速缓存行的更多相邻路径)。

它确实将您的无符号索引范围从 2^31 减少到 2^30(允许大约十亿个迷宫图块)。您可以使用 unsigned int 将该范围加倍到 2^31(尽管可能不值得这么麻烦,除非您实际上打算处理接近该比例的东西)。