我正在编写一个编译器并拥有大量标志。在大多数情况下,我的节点将收到非常少量的标志(最大值为12),但标志总数相当大(超过50)。所有标志都是枚举中定义的整数:
enum flags_t
{
FLAG_ONE,
FLAG_TWO,
FLAG_THREE,
[...]
MAX_FLAG
};
我认为使用std::map<flags_t, bool>
更有意义,因为我的大多数节点都可能使用0,1或2个标志,节点数量非常大(很容易变成十分之一。 )
// with a map we have to check the existing on a get to avoid creating
// useless entries in the map
bool node::get_flag(flags_t const f)
{
flag_iterator it(f_flags.find(f));
return it == f_flags.end() ? false : *it;
}
void node::set_flag(flags_t const f, bool const value)
{
f_flags[f] = value;
}
但我想知道std::vector<bool>
是否真的不会更有效?虽然乍一看这看起来不错:
bool node::get_flag(flags_t const f)
{
return f_flags[f];
}
void node::set_flag(flags_t const f, bool const value)
{
f_flags[f] = value;
}
需要在初始化时分配(即正确调整大小)向量,或者get_flag()函数需要测试f是否是向量的一部分:
bool node::get_flag(flags_t const f)
{
return f >= f_flags.size() ? false : f_flags[f];
}
我可以通过resize()调用看到的问题是我们会一直分配/释放内存,即使我们最终没有实际使用向量(大多数节点不需要任何标志!)所以测试当我们做get时限制可能是一个很好的权衡,但我们还需要确保向量在set_flag()调用上足够大...(在这种情况下,我们可能会分配整组标志一次,以避免重新分配。)
bool node::set_flag(flags_t const f, bool const value)
{
if(MAX_FLAG > f_flags.size())
{
f_flags.resize(MAX_FLAG);
}
f_flags[f] = value;
}
那么...... std::vector
或std::map
会更好吗?或者std::set
可能会更好? (我之前没有使用过std :: set)。
答案 0 :(得分:4)
std::set
和std::map
都是标记的次优选择,因为它们动态分配存储,导致不必要的碎片。
表示标志的简单方法是将它们存储为整数类型。无符号64位类型将为64个标志提供空间。这将既节省空间又节省CPU,并且可以启动惯用的C ++。例如:
enum flag_code
{
FLAG_ONE = 1ULL << 0,
FLAG_TWO = 1ULL << 1,
FLAG_THREE = 1ULL << 2,
[...]
};
typedef uint64_t flags_t;
void node::set_flag(flag_code f, bool value)
{
if (value)
f_flags |= f;
else
f_flags &= ~f;
}
bool node::get_flag(flag_code f)
{
return bool(f_flags & f);
}
如果需要超过64个标志,则最好用std::bitset
表示位操作,它还提供对基础值的各个位的类似数组的访问:
enum flag_code
{
FLAG_ONE,
FLAG_TWO,
FLAG_THREE,
[...]
MAX_FLAG
};
typedef std::bitset<MAX_FLAG - 1> flags_t;
void node::set_flag(flag_code f, bool value)
{
f_flags[f] = value;
}
bool node::get_flag(flag_code f)
{
return f_flags[f];
}