我正在使用 SFML 创建贪吃蛇游戏,我有一个具体类 SnakeGame
它包含我类的所有数据成员,例如窗口大小、游戏 map 的大小、蛇的颜色等。因此,我类的内部成员如下所示:
class SnakeGame
{
private:
//....
const sf::Vector2u windowSize{400, 336};
const sf::Color snakeColor {0, 0, 0};
//...etc...
public:
SnakeGame() :
renderWindow {sf::VideoMode{windowSize.x, windowSize.y}, /*other arguments*/ },
snake {snakeColor, /*other arguments*/}
/*etc*/
{}
但是,这不起作用,我得到一个不可见的窗口,使我相信参数尚未构建。在阅读了另一篇 SO 帖子后,我读到了 const
作为类成员的成员不会在编译时求值,而是在类被实例化时在运行时求值,否则它们的行为方式与 const
完全相同。变量。因此,变量 windowSize
和 snakeColor
在我实例化 SnakeGame
时进行评估在main
,所以我不能在我的类的初始化列表中使用它们。为了尝试解决这个困境,我决定切换到 static constexpr
变量,但不幸的是我意识到 sf::Vector<T>
没有 constexpr
构造函数,sf::Color
也没有.当然,我想我可以切换到整数类型,例如 static constexpr unsigned int
对于窗口大小,但最终这会变得重复,尤其是对于颜色。所以我决定走另一条路。我所做的是创建另一个文件 GameData.hpp
,并在其中执行此操作:
namespace gd //For game data
{
const sf::Vector2u windowSize{400, 336};
const sf::Color snakeColor {0, 0, 0};
//...etc...
}
但是,我不喜欢这个解决方案,因为即使现在在编译时评估此数据,如果它们包含适当的头文件,我的所有类也可以访问它,而不仅仅是 SnakeGame
。 .这样一来,感觉我类的内部数据结构就暴露了。这引出了我的问题,即:有没有办法强制 const
要在编译时以某种方式评估类的成员,以便这些变量可以在初始化列表中使用,并保证它们将被构造?
最佳答案
这里有几个选项,所有选项都需要权衡取舍。我按照我认为最好到最差的粗略顺序对它们进行了排名,但您的权衡可能会有所不同。
- 重构您的代码,使颜色和窗口大小不再是全局常量。作为一名玩家,我想要一条绿蛇,而不是一条黑蛇,我想要一个两倍大的棋盘。这可能涉及将它们保留为成员变量,但这次不是常量,并且可能对它们重新排序,以便您可以在其他成员变量的初始化中使用它们。
- 将编译器设置为 C++17 模式,并将
snakeColor
设置为inline static const
变量。如果您想要一个仅包含 header 的库并且可以使用 C++17,那么这是最佳选择。 - 使其成为
const static
数据成员,并将const sf::Color SnakeGame::snakeColor {0, 0, 0}
添加到恰好一个。 cpp
文件。如果您不想要仅包含 header 的库,这是最佳选择。 - 制作一个
getColor
static const sf::Color& getSnakeColor()
方法,里面有一个static
常量它,并返回它。static
关键字在函数内部的含义略有不同,您可以在那里做您想做的事。但是,这意味着您需要更改使用它的语法。 - 按照您在回答中的建议使用命名空间。如果其他所有内容都在
namespace snake
中,那么您将这些变量放在snake::details
中,然后按照惯例人们会知道他们不应该看那里。不过,与其他选项不同的是,这并不是强制执行的。 - 使用 SFML 提交功能请求,以将
Vector2u
和Color
转换为文字类型,以便您可以使用constexpr
。显然,这需要更长的时间,而且不能保证一定有效。 - 重新排序类以更有用的顺序排列成员,as suggested by VTT in the comments .这与第一个选项相同,我说过我最喜欢它,但是将它们作为常量,对于类的每个实例都具有相同的值。我将它放在列表底部的原因是,这意味着您的类的每个拷贝 都将带有颜色和窗口大小的拷贝,而您实际上只需要一个。
可能还有其他选项我忘记了。例如,有一种涉及辅助模板类的方法,您可以使用它来获取仅包含 header 的库,而无需 inline
成员变量支持,也无需切换到函数调用来访问数据,并且强制执行 private
说明符的地方。不过,我不太记得如何让它发挥作用,而且它比通常值得的要复杂得多。
关于c++ - 强制类的 const 成员在编译时进行评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45152302/