V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
northisland
V2EX  ›  程序员

一点心得,使用联合体 union 存储用户选项的方法

  •  
  •   northisland · 2019-03-26 08:09:00 +08:00 · 1918 次点击
    这是一个创建于 2073 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在使用 C++设计一个存储用户选项的结构体(类)时,往往会遇到 a 选项, b 选项等包含的信息数据不一致的问题。

    如果为 a、b、c 等选项强行设计统一的数据结构体,再统一写处理逻辑。个人感觉比较困难:

    • 原因 1. 统一的数据结构,需要照顾所有选项,维护成本高;
    • 原因 2. 写处理逻辑时也会涉及复杂的检查;

    所以,在业务没完全定型,在需要 quick and dirty 搞定该问题相关的业务时,个人认为联合体(union)适合处理该类问题。

    举个例子: 假设有一个画图表的需求,用户有 2 个选项:

    • a. 由程序自动确认绘制范围;
    • b. 用户制定绘制某个区间;

    存储用户选择的数据结构中,a, b 中除了状态标志( is_auto_x )外,还有各选项独有的数据(本例体现在 b 选项会引入起始点 x_start,终止点 x_end,然而这些数据对 a 选项并无意义)。

    定义选项数据如下

    // 用户选项结构体
    struct Cfg {
        union X_Status {
            bool is_auto_x;                      // a 选项相关数据
            struct X_Range_Setting x_setting;    // b 选项相关数据
        } x_status;                              // 用此联合体表示互斥的选项
        // ……其他数据和方法,略
    };
    // b 选项相关数据
    struct X_Range_Setting {
        bool is_auto_xrange;    // 标志位统一
        float x_start;
        float x_end;
    };
    

    处理代码逻辑如下:

    switch (x_status.is_auto_x) {
        case true: // 处理用户 a 选项,具体访问`x_status.is_auto_x`
            ...; break;
        case false:  // 处理用户 b 选项,具体访问`x_status.x_setting`
            ...; break;
    }
    

    demo 传送门在这

    另外值得注意的是:

    • 由于 c++里联合体是多个类共用一块数据内存,可以解释成所有类,因此,这些类不能包含构造、析构函数。
    • 想用构造函数创建该选项对象,得自己写子类了。

    这 2 点 demo 里也有一点体现。

    6 条回复    2019-03-26 14:05:35 +08:00
    AngelCriss
        1
    AngelCriss  
       2019-03-26 08:56:08 +08:00 via Android
    注意 UB,建议 variant
    haozhang
        2
    haozhang  
       2019-03-26 09:08:05 +08:00 via Android   ❤️ 1
    union 的设计不就是为了解决这类问题吗....
    northisland
        3
    northisland  
    OP
       2019-03-26 12:45:49 +08:00 via Android
    @haozhang 个人观察,我身边的朋友,很少总联合体这个 c 里就有的老结构,去做堆栈里类的封装。
    northisland
        4
    northisland  
    OP
       2019-03-26 12:50:27 +08:00 via Android
    @AngelCriss UB 是什么缩写啊?
    northisland
        5
    northisland  
    OP
       2019-03-26 13:01:01 +08:00 via Android
    @haozhang 是的,联合体主要解决互斥数据在堆栈的储存,访问问题。

    仔细想了一下,用同父类的子类,来解决这个问题,弄个 Base_Choice,和一堆子类 Choice_A, Choice_B 来解决,和用联合体效果一下,只是内存用的稍多。但用同父类的子类应该是主流了。
    no1xsyzy
        6
    no1xsyzy  
       2019-03-26 14:05:35 +08:00   ❤️ 1
    @northisland #4 应该是 Undefined behavior
    #3 因为 union 和 struct 需要反复封装,而且 union 本身不包含类型信息,需要自己实现一个 struct 来封装成 variant,实在是一个不完全的 Union。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3708 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 10:43 · PVG 18:43 · LAX 02:43 · JFK 05:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.