2

我是一个相对的 C/C++ 菜鸟,但在 C# 和许多其他语言方面经验丰富,目前正在用 C++ 构建游戏引擎。

对于输入系统(键/鼠标/操纵杆),我有一个想法,即使用字符串文字来简化游戏特定输入事件的配置,而不是枚举。但我不完全确定 C 编译器将如何优化它 - 每次进行逐个字符的字符串比较可能效率低下。

这是基本思想 - 从设备事件到“游戏特定事件”的映射将在 std::map 中,带有 int/enum 键和 const char * 值:

Mappings[ KEY_X ] = "Jump";
Mappings[ KEY_Y ] = "Shoot";
Mappings[ MOUSE_1 ] = "Shoot";

这些可以是硬编码的,从配置文件加载,等等(字符串将简化解析和保存,以及添加新的事件类型)

然后在游戏中,我可以只处理游戏特定的事件:

if ( IsDown( "Jump" ) ) {
}
if ( IsSinglePress( "Shoot" ) ) {
}

问题是,我所有的字符串文字和 const char * 会被神奇地优化掉并最终像枚举或整数常量一样高效吗?例如

if ( IsDown( GAMEKEY_SHOOT ) ) {
   // ...
}

还是会进行字符串比较等?我希望编译器会看到相同字符串文字的实例,将其存储一次,并在整个过程中使用单个指针值,但不确定。

4

4 回答 4

3

如果所有字符串常量都在同一个文件中,它们可能会被折叠到同一个内存位置。如果它们位于单独编译的多个文件中,或者如果您动态读取它们,则它们不会。

由于您正在使用 C(++),因此您可以通过两种方式比较字符串(并且您在问题中没有提及任何内容):指针标识 ( ==) 或逐字符比较 ( strncmp)。如果你做的是前者,它是非常不可靠的,因为它取决于编译器的变化无常(并且在第二种情况下肯定会失败);如果是后者,那么您知道将进行字符串比较。

省去自己的麻烦,并以您已经知道正确的方式去做:枚举或常量。在输入/输出处转换一次,然后您可以在内部将它们作为数字处理,这既快速又安全。

于 2012-12-10T00:15:30.467 回答
2

您提到了几次在枚举和字符串之间进行转换。这是我的做法。(我认为这个想法归功于 gcc。)创建一个像 action.def 这样的文件

ENUM(Jump)
ENUM(Shoot)

在头文件中定义这样的枚举

#define ENUM(a) a,
Enum Actions {
#include "action.def"
};
#undef ENUM

在需要将枚举成员映射到字符串的 .cpp 文件中,执行此操作

struct EnumMap {
    int value;
    const char * name;
}
#define ENUM(a) { (int) a, #a },
struct EnumMap {
#include "action.def"
} ActionMap[];
#undef ENUM

然后您的代码使用 ActionMap[] 将枚举值转换为字符串并返回。

于 2012-12-10T01:39:39.200 回答
1

就标准而言,编译器可能会也可能不会使相同字符串文字的各种实例指向相同的内存位置

是否所有字符串文字都是不同的(即,存储在不重叠的对象中)是实现定义的。

(C++11,§2.14.5 ¶12)

此外,如果这些值是动态分配的,来自某个缓冲区,......它们肯定有与字符串文字不同的地址;所以检查一个是否等于另一个的唯一明智的方法是比较字符串的字节(以strcmp类似的方式),这显然比指针比较慢。

此外,我看不出在这些设置中使用字符串的意义:只使用enums,它的执行速度至少一样快(可能更快)并且更不容易出错(如果你写了错误的枚举值,你会得到一个编译错误,如果您在字符串常量中打错字,它会被静默接受)。

于 2012-12-10T00:14:22.863 回答
1

无论如何,多次写出相同的字符串文字很容易出错 - 如果您错误地输入其中一个怎么办?

如果您使用全局变量,您可以确保它们具有相同的地址,因为您指的是完全相同的变量:

char const * const Jump = "Jump";

(或在标头中声明并在单个 .cpp 中初始化实际字符串值)

并用作:

Mappings[ KEY_X ] = Jump;
...
if ( IsDown( Jump ) ) {
    ...

我不会假装这是很棒的风格(它不是),如果你只是想要一个枚举和字符串表示之间的内置链接,有更好的方法来获得它。

于 2012-12-10T00:19:31.410 回答