我是一名相对的C / C ++ noob,但在C#和其他一些语言方面经验丰富,目前正在使用C ++构建游戏引擎。
对于输入系统(键/鼠标/操纵杆),我有一个想法是使用字符串文字来简化游戏特定输入事件的配置,而不是枚举。但我并不完全确定C编译器如何优化它 - 每次进行逐字符串字符串比较可能效率低下。
这是基本的想法 - 从设备事件到“游戏特定事件”的映射将在带有int / enum键和const char *值的std :: map中:
Mappings[ KEY_X ] = "Jump";
Mappings[ KEY_Y ] = "Shoot";
Mappings[ MOUSE_1 ] = "Shoot";
这些可以是硬编码的,从配置文件中加载等等(字符串可以简化解析和保存,以及添加新的事件类型)
然后在游戏中,我只能处理游戏特定事件:
if ( IsDown( "Jump" ) ) {
}
if ( IsSinglePress( "Shoot" ) ) {
}
问题是,我的所有字符串文字和const char *都会被神奇地优化掉,最终会像枚举或整数常量一样高效吗? E.g。
if ( IsDown( GAMEKEY_SHOOT ) ) {
// ...
}
或者它会进行字符串比较等吗?我希望编译器能看到相同字符串文字的实例,存储一次,并在整个过程中使用单个指针值,但不确定。
答案 0 :(得分:3)
如果所有字符串常量都在同一个文件中,它们可能会折叠到同一个内存位置。如果它们在几个单独编译的文件中,或者如果你动态地读取它们,它们就不会。
由于你正在做C(++),你可以用两种方式比较字符串(你在问题中没有说过任何关于它的东西):指针标识(==
)或字符串 - 字符比较(strncmp
)。如果你正在做前者,它是非常不可靠的,因为它取决于编译器的变化(并且在第二种情况下肯定会失败);如果是后者,那么你就知道将进行字符串比较。
让自己避免头痛,并以正确的方式做到这一点,你已经知道这是正确的方式:枚举或常量。在输入/输出转换一次,然后您可以在内部处理它们,这是快速和安全的。
答案 1 :(得分: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 []将枚举值转换为字符串并返回。
答案 2 :(得分:1)
就标准而言,编译器可能会也可能不会使相同字符串文字的各种实例指向相同的内存位置
是否所有字符串文字都是不同的(即存储在非重叠对象中)是实现定义的。
(C ++ 11,§2.14.5¶12)
另外,如果值是动态分配的,来自某个缓冲区,......它们肯定有不同的字符串文字地址;所以检查一个是否与另一个相等的唯一理智的方法是比较字符串的字节(以strcmp
- 类似方式),这显然比指针比较慢。
此外,我没有看到使用字符串进行这些设置的重点:只使用enum
s,它至少执行速度快(可能更快),并且它更不容易出错(如果你写一个错误的枚举值会导致编译错误,如果你在字符串常量中输入错误,则会默默地接受它。)
答案 3 :(得分:1)
多次写出相同的字符串文字很容易出错 - 如果你误输了其中一个怎么办?
如果使用全局变量,您可以确定它们具有相同的地址,因为您指的是完全相同的变量:
char const * const Jump = "Jump";
(或在标题中声明并在单个.cpp中初始化实际的字符串值)
并用作:
Mappings[ KEY_X ] = Jump;
...
if ( IsDown( Jump ) ) {
...
我不会假装这是一种很好的风格(它不是),如果你只想在枚举和字符串表示之间建立内置链接,那么有更好的方法来获取它。