Trick này được gọi là “Bitmasking” aka “bitwise flags” aka “bit flag pattern” aka “flag enumeration“
Thay vì tạo nhiều biến booleans (true/false) độc lập, chúng ta có thể lưu tất cả chúng vào 1 biến integer và thao tác nó bằng bitand
Ưu điểm
- Compact storage: một số integer có thể biểu diễn nhiều flags
- Fast checks: bitwise operations cực kì nhanh
- Flexible combinations: có thể on/off từng flag mộ cách độc lập mà không ảnh hưởng đến các flags khác
Mỗi giá trị (vd: 1 , 2, 4, 8, 16, …) là một lũy thừa của 2 (vd: 20 (bit thứ nhất bật), 21 (bit thứ hai bật),…) tương ứng với một bit duy nhất được bật (1), vì thế chúng ta có thể kết hợp chúng bằng phép cộng (hoặc bitwise OR) và kiểm tra chúng bằng bitwise AND.
const int READ = 1; // 00000001 bit 0 bật
const int WRITE = 2; // 00000010 bit 1 bật (dịch bit 1 sang trái một vị trí)
const int EXEC = 4; // 00000100
// combine
00000001 + 00000010 = 00000011 (decimal 3).
// check
bitand(3, 1) = 1 -->TRUE
// Khi khai báo biến hoặc hàm là static trong struct/class, chúng thuộc về struct/class chứ không thuộc về một đối tượng cụ thể.
// Điều này cho phép gọi trực tiếp Permissions::READ, Permissions::setFlag() mà không cần khởi tạo Permissions permision;
struct Permissions
{
static const int READ = 1;
static const int WRITE = 2;
static const int EXEC = 4;
static const int DEFAULT = READ + WRITE;
// Utility functions
// thêm hoặc xóa flag
static void setFlag(int& p_mode, int p_flag, bool p_value)
{
if (p_value){
p_mode |= p_flag; // Set flag (bitwise OR ensures flag is added)
} else {
p_mode &= ~p_flag; // Reset flag (bitwise AND with NOT removes flag)
}
}
// Kiểm tra flag
static bool isFlagSet(int p_mode, int p_flag)
{
return (p_mode & p_flag) == p_flag;
}
// in flags
static string getFlagNames( int p_mode )
{
// create a string with " | flag" for each raised bit flag
string l_result = string( p_mode & READ ? "|READ" : "" )
.append( p_mode & WRITE ? "|WRITE" : "" )
.append( p_mode & EXEC ? "|EXEC" : "" );
// remove leading " | " if present
return l_result.empty() ? l_result : l_result.substr(1);
};
}
int main() {
int mode = Permissions::DEFAULT;
cout << "Initial mode: " << mode << endl;
// add flag
setFlag(mode, Permissions::EXEC, true);
// check flag
if (isFlagSet(mode, Permissions::EXEC)) {
cout << "Permission::EXEC is set, current mode: "<< mode << endl;
}
cout << "Final mode: " << Permissions::getFlagNames(mode) << endl;
return 0;
}
Initial mode: 3
Permission::EXEC is set, current mode: 7
Final mode: READ|WRITE|EXEC