很多时候,我希望能用一个变量方便的表示多种状态.例如我有一个窗口类,要表示此窗口是否具有系统菜单,最小化按钮,最大化按钮,帮助按钮,可能需要四个变量来记录.
bool hasSystemMenu;
bool hasMinimize;
bool hasMaximize;
bool hasHelp;
这样要占用更多的内存.虽然用位域可以节约内存,但操作不方便.
DELPHI这点就做得不错,用set of 关键字声明一个集合类型数据.如下:
TWindowButton = (hasSystemMenu, hasMinimize, hasMaximize, hasHelp);
TWindowButtonSet = set of TWindowButton;
使用也很方便,并且高效.代码和对应的汇编代码如下:
procedure TForm1.Button1Click(Sender: TObject);
var
Button: TWindowButtonSet;
begin
Include(Button, hasMinimize); or bl, 02
Include(Button, hasMaximize); or bl, 04
Exclude(Button, hasMaximize); and bl, $fb
if hasMinimize in Button then test bl, 02
jz xxxx
...
end;
很直观,用C++也能实现,我首先想到的办法是声明一个枚举,利用枚举能转换为整型的特性,针对枚举类型进行集合操作.例如
enum TestEnum {T1, T2, T3, T4};
使用时大概这样, set = set | (1 << T1)
set = set & (0xFFFFFFFF ^ (1 << T2))
这不够好,参与set运算的值都是运行期确定的,而DELPHI的set类型参与运算的值是编译期确定的,还好我们有模板,能够实现编译期求值,定义如下:
template<int Value>
class EnumValue
{
public:
enum { size = 1 << Value };
enum { nsize = 0xFFFFFFFF ^ (1 << Value) };
};
来试试 cout << EnumValue<T3>::size ; 输出4
呵呵,OK,剩下的就是写代码的工作了.完成版如下,效率不比DELPHI的低,缺点是只支持最大32个成员
/*
by 剑神一笑
模拟DELPHI的Set数据单元
*/
#ifndef P_SET_H
#define P_SET_H
//给出1右移Value次的值,使用模板是为了在编译期能求值
template<int Value>
class EnumValue
{
public:
enum { size = 1 << Value };
enum { nsize = 0xFFFFFFFF ^ (1 << Value) };
};
template<typename T>
class EnumSet
{
private:
int _value;
public:
explicit EnumSet(bool contain)
{
//是否初始化时就包含所有成员
if (contain)
_value = 0xFFFFFFFF;
else
_value = 0;
}
//包含一个枚举,用模板传参而不用参数是为了
//能在编释期确定值,提高效率
template<T Value>
void Include()
{
_value |= EnumValue<Value>::size;
}
//去掉一个枚举
template<T Value>
void Exclude()
{
_value &= EnumValue<Value>::nsize;
}
//判断是否包含一个枚举
template<T Value>
bool Has()
{
return _value & EnumValue<Value>::size;
}
//判断set是否是空的
bool Empty()
{
return _value == 0;
}
};
#endif//P_SET_H
-------------------------------------------分隔线--------------------------
测试代码:
#include <iostream>
#include <string>
#include <math.h>
#include "PSet.h"
using std::cin;
using std::cout;
using std::endl;
enum TestEnum {hasSystemMenu, hasMinimize, hasMaximize, hasHelp};
int main()
{
EnumSet<TestEnum> set(false);
set.Include<hasSystemMenu>();
set.Include<hasMaximize>();
set.Exclude<hasSystemMenu>();
if (set.Has<hasSystemMenu>())
cout << "hasSystemMenu" << endl;
if (set.Has<hasMinimize>())
cout << "hasMinimize" << endl;
if (set.Has<hasMaximize>())
cout << "hasMaximize" << endl;
cin.get();
return 0;
}