位运算做标记的好处
很多底层的东西都喜欢用 对1位移N位来做枚举或标记,是因为这样可以同时保存多个状态进这个标记
以这个枚举为例(虚幻源码中垃圾回收对象的标记枚举,这里把位移n位改为了1,2,3,4…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14enum class EInternalObjectFlags : int32
{
None = 0,
// All the other bits are reserved, DO NOT ADD NEW FLAGS HERE!
ReachableInCluster = 1 << 1, /// External reference to object in cluster exists
ClusterRoot = 1 << 2, ///< Root of a cluster
Native = 1 << 3, ///< Native (UClass only).
Async = 1 << 4, ///< Object exists only on a different thread than the game thread.
AsyncLoading = 1 << 5, ///< Object is being asynchronously loaded.
Unreachable = 1 << 6, ///< Object is not reachable on the object graph.
PendingKill = 1 << 7, ///< Objects that are pending destruction (invalid for gameplay but valid objects)
RootSet = 1 << 8, ///< Object will not be garbage collected, even if unreferenced.
NoStrongReference = 1 << 9,
};初始化个变量
1
EInternalObjectFlags Flags = EInternalObjectFlags::Native; //也就是Flags = 00010000
假设有这几个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25FORCEINLINE void SetPendingKill()
{
Flags |= int32(EInternalObjectFlags::PendingKill);
}
FORCEINLINE void ClearPendingKill()
{
Flags &= ~int32(EInternalObjectFlags::PendingKill);
}
FORCEINLINE bool IsPendingKill() const
{
return !!(Flags & int32(EInternalObjectFlags::PendingKill));
}
FORCEINLINE void SetRootSet()
{
Flags |= int32(EInternalObjectFlags::RootSet);
}
FORCEINLINE void ClearRootSet()
{
Flags &= ~int32(EInternalObjectFlags::RootSet);
}
FORCEINLINE bool IsRootSet() const
{
return !!(Flags & int32(EInternalObjectFlags::RootSet));
}状态改变过程
- SetPendingKill, Flags = 10010000,加入了PendingKill状态,此时有PendingKill和Native两种状态
- IsPendingKill, ret = 10010000 & 10000000 = 10000000 = true,判断是否有 PendingKill 状态
- ClearPendingKill, Flags = 10010000 & 0111111 = 00010000,清除了PendingKill状态,此时只有 NativeNative 状态
- 其他以此类推
作用场景
- 用于同一个标记Flag中需要保存多种状态,否者用普通的枚举1,2,3…就可以。