c++ - 是否值得使用位移位将多个小数据成员存储在一个字节中?

标签 c++

在 C++ 中,任何对象或原始数据类型的最小大小都是 1 个字节。但是,我经常使用只有几个可能值的枚举类型。它最近出现在我的一门类(class)的项目中,我必须存储许多包含两种不同的小型枚举类型的结构。因此,当然,我将枚举类型的基础类型设为 unsigned chars,并将结构设为每 2 个字节。

但是,由于每个枚举类型的可能值远少于 16 个,我意识到我可以使用位移来将它们存储在仅 1 个字节中。

这就是我所说的:

enum utensil : unsigned char {fork, spoon, spork};
enum dish : unsigned char {plate, bowl, box};
enum food : unsigned char {soup, salad, entree};
enum dessert: unsigned char {cake, ice_cream, fudge};

/* A class containing one of each of the four enums 
above, but only taking up 1 byte of memory */

class TakeOut{
  private:
    unsigned char data = 0;

    void clear_utensil(){
       data = data & 0b00111111;
    }
    void clear_dish(){
       data = data & 0b11001111;
    }
    void clear_food(){
       data = data & 0b11110011;
    }
    void clear_dessert(){
       data = data & 0b11111100;
    }
  public:
    utensil get_utensil() const{
       return utensil((data & 11000000) >> 6);
    }
    dish get_dish() const{
       return dish((data & 00110000) >> 4);
    }
    food get_food() const{
       return food((data & 00001100) >> 2);
    }
    dessert get_dessert() const{
       return dessert(data & 00000011);
    }
    void set_utensil(utensil in){
       clear_utensil();
       data = data | ((unsigned char)(in) << 6);
    }
    void set_dish(dish in){
       clear_dish();
       data = data | ((unsigned char)(in) << 4);
    }
    void set_food(utensil in){
       clear_food();
       data = data | ((unsigned char)(in) << 2);
    }
    void set_dessert(utensil in){
       clear_dessert();
       data = data | (unsigned char)(in);
    }
};

如果机会再次出现,我是否应该避免在“真实”项目中这样做?这当然很复杂,但如果我必须存储大量 TakeOut 对象,那么在访问数据成员时牺牲一点时间也许是值得的。

最佳答案

你不需要为此使用移位,你可以在结构中使用位域。这为您提供了相同的功能,但大大减少了输入(这意味着更少的维护、更少的错误和更多的乐趣!):

enum utensil_t : unsigned char {fork, spoon, spork};
enum dish_t : unsigned char {plate, bowl, box};
enum food_t : unsigned char {soup, salad, entree};
enum dessert_t: unsigned char {cake, ice_cream, fudge};

struct TakeOut {
    utensil_t utensil : 2;
    dish_t dish : 2;
    food_t food : 2;
    dessert_t dessert : 2;
};

但是,请注意,当您执行此操作时,您是在用性能换取规模,而且您很可能不希望进行此交易。除非你在一个非常受限的环境中处理很多这样的问题。

Language-lawyer note:从技术上讲,C++ 编译器不是需要打包位域,因为它们的布局是实现定义的。实际上,我认为在实践中没有任何不打包位域的实现。您可以通过以下方式轻松保护自己免受疯狂实现的影响:

static_assert(sizeof(TakeOut) == 1, "Sanity, please!");

关于c++ - 是否值得使用位移位将多个小数据成员存储在一个字节中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58087364/

相关文章:

c++ - 为什么在某些情况下可以调用没有对象的成员函数?

c++ - 在 C++ 中查找程序的运行时间

c++ - 动态内存分配/利用未使用的内存

c++ - 避免缩小 c 样式数组创建时的警告

c++ - 使用模板输出过载

c++ - 未找到 libpng "png_set_longjmp_fn"

c++ - 如何居中QMainWindow?

c++ - 为什么下面程序的输出是 12 字节,而总大小只有 6 位?

c++ - 编译时是否需要短路评估规则?

c++ - 缓冲区用不需要的数据填充 char 数组中的最后一个空白区域