我应该使用#define、enum 还是const?
在我正在处理的 C++ 项目中,我有一个 flag 类型的值,它可以有四个值.这四个标志可以组合在一起.标志描述数据库中的记录,可以是:
In a C++ project I'm working on, I have a flag kind of value which can have four values. Those four flags can be combined. Flags describe the records in database and can be:
- 新记录
- 删除记录
- 修改记录
- 现有记录
现在,对于我希望保留此属性的每条记录,我可以使用枚举:
Now, for each record I wish to keep this attribute, so I could use an enum:
enum { xNew, xDeleted, xModified, xExisting }
但是,在代码的其他地方,我需要选择哪些记录对用户可见,因此我希望能够将其作为单个参数传递,例如:
However, in other places in code, I need to select which records are to be visible to the user, so I'd like to be able to pass that as a single parameter, like:
showRecords(xNew | xDeleted);
所以,我似乎有三种可能的方法:
So, it seems I have three possible appoaches:
#define X_NEW 0x01
#define X_DELETED 0x02
#define X_MODIFIED 0x04
#define X_EXISTING 0x08
或
typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;
或
namespace RecordType {
static const uint8 xNew = 1;
static const uint8 xDeleted = 2;
static const uint8 xModified = 4;
static const uint8 xExisting = 8;
}
空间要求很重要(byte vs int)但不是关键.使用定义我失去了类型安全性,而使用 enum
我失去了一些空间(整数)并且当我想要进行按位运算时可能不得不强制转换.使用 const
我想我也失去了类型安全性,因为随机的 uint8
可能会错误地进入.
Space requirements are important (byte vs int) but not crucial. With defines I lose type safety, and with enum
I lose some space (integers) and probably have to cast when I want to do a bitwise operation. With const
I think I also lose type safety since a random uint8
could get in by mistake.
还有其他更干净的方法吗?
Is there some other cleaner way?
如果不是,你会用什么,为什么?
If not, what would you use and why?
附言其余的代码是相当干净的现代 C++,没有 #define
s,我在几个空格中使用了命名空间和模板,所以这些也不是没有问题.
P.S. The rest of the code is rather clean modern C++ without #define
s, and I have used namespaces and templates in few spaces, so those aren't out of question either.
推荐答案
组合策略以减少单一方法的缺点.我在嵌入式系统中工作,因此以下解决方案基于整数和按位运算符快速、低内存和低内存的事实.闪存使用率低.
Combine the strategies to reduce the disadvantages of a single approach. I work in embedded systems so the following solution is based on the fact that integer and bitwise operators are fast, low memory & low in flash usage.
将枚举放在命名空间中,防止常量污染全局命名空间.
Place the enum in a namespace to prevent the constants from polluting the global namespace.
namespace RecordType {
枚举声明并定义编译时检查类型.始终使用编译时类型检查来确保参数和变量的类型正确.C++ 中不需要 typedef.
An enum declares and defines a compile time checked typed. Always use compile time type checking to make sure arguments and variables are given the correct type. There is no need for the typedef in C++.
enum TRecordType { xNew = 1, xDeleted = 2, xModified = 4, xExisting = 8,
为无效状态创建另一个成员.这可以用作错误代码;例如,当您想返回状态但 I/O 操作失败时.它也可用于调试;在初始化列表和析构函数中使用它来知道是否应该使用变量的值.
Create another member for an invalid state. This can be useful as error code; for example, when you want to return the state but the I/O operation fails. It is also useful for debugging; use it in initialisation lists and destructors to know if the variable's value should be used.
xInvalid = 16 };
考虑到这种类型有两个目的.跟踪记录的当前状态并创建掩码以选择处于某些状态的记录.创建一个内联函数来测试该类型的值是否符合您的目的;作为状态标记与状态掩码.这将捕获错误,因为 typedef
只是一个 int
并且诸如 0xDEADBEEF
之类的值可能通过未初始化或错误指向的变量存在于您的变量中.
Consider that you have two purposes for this type. To track the current state of a record and to create a mask to select records in certain states. Create an inline function to test if the value of the type is valid for your purpose; as a state marker vs a state mask. This will catch bugs as the typedef
is just an int
and a value such as 0xDEADBEEF
may be in your variable through uninitialised or mispointed variables.
inline bool IsValidState( TRecordType v) {
switch(v) { case xNew: case xDeleted: case xModified: case xExisting: return true; }
return false;
}
inline bool IsValidMask( TRecordType v) {
return v >= xNew && v < xInvalid ;
}
如果您想经常使用该类型,请添加 using
指令.
Add a using
directive if you want to use the type often.
using RecordType ::TRecordType ;
值检查函数在断言中很有用,可以在使用时立即捕获错误值.运行时发现错误的速度越快,它造成的损害就越小.
The value checking functions are useful in asserts to trap bad values as soon as they are used. The quicker you catch a bug when running, the less damage it can do.
这里有一些例子可以把它们放在一起.
Here are some examples to put it all together.
void showRecords(TRecordType mask) {
assert(RecordType::IsValidMask(mask));
// do stuff;
}
void wombleRecord(TRecord rec, TRecordType state) {
assert(RecordType::IsValidState(state));
if (RecordType ::xNew) {
// ...
} in runtime
TRecordType updateRecord(TRecord rec, TRecordType newstate) {
assert(RecordType::IsValidState(newstate));
//...
if (! access_was_successful) return RecordType ::xInvalid;
return newstate;
}
确保正确值安全的唯一方法是使用带有运算符重载的专用类,这留给其他读者作为练习.
The only way to ensure correct value safety is to use a dedicated class with operator overloads and that is left as an exercise for another reader.
相关文章