c++ - 如何在 C++ 中实现带有变体字段的简单类

标签 c++ variant

我想实现类,假设有字段键和类 A 或 B。 此类的构造函数中的参数是字符数组。 构造函数伪代码首先查看 char,如果它与 0x00 相同,它将创建 A 类对象, 否则它将创建 B 类对象——两个类都将字符数组作为参数。

无论如何,我想让这个实现保持简单。我不想使用 boost::Variant 除非我真的需要, 而且我也不想像这样实现某事 Implementing a "variant" class 因为我不熟悉模板编程,我认为我的问题可以用更简单的方式实现。

最佳答案

对于 POD 类型,我们有 union (但 union 不会记住你指定​​的 类型,因此也将其单独存储)。这不适用于非 POD 类型。主要原因是因为 C++ 不知道应该在构造时创建 union 还是在删除 union 时删除。

但是 union 可以用来保存指向实际类型的指针。那你就得自己关心构造和删除了。

你可以创建这样的东西,它包装这个指针 union 并添加一个方便的接口(interface)。详细解释写在评论里:

class EitherAorB {
    // We have to remember what we actually created:
    enum Which {
        A_Type,
        B_Type
    } m_which;

    // We store either a pointer to an A or to a B. Note that this union only
    // stores one pointer which is reused to interpret it as an A*, B* or void*:
    union {
        A *a;
        B *b;
        void *untyped; // Accessing the same pointer without looking at the type
    } m_ptr;

    // Additional stuff you want to store besides A and B
    const char *m_key;

public:
    EitherAorB(const char *key) {
        // Decision: Which type do we want to create?
        m_which = key[0] == 0 ? A_Type : B_Type;
        // Create the type (the cast to void* make the pointer "untyped"):
        m_ptr.untyped = m_which == A_Type ? (void*)new A() : (void*)new B();

        // Store additional stuff
        m_key = key;
    }
    ~EitherAorB() {
        // Since we stored the actual contents outside and point to them,
        // we have to free the memory. For this, we have to care about the
        // type again, so the correct destructor will be chosen. Deleting
        // the untyped pointer won't work here.
        if (m_which == A_Type) delete m_ptr.a;
        if (m_which == B_Type) delete m_ptr.b;
    }

    // These two functions can be used to query which type is stored.
    bool hasA() const {
        return m_which == A_Type;
    }
    bool hasB() const {
        return m_which == B_Type;
    }

    // These two functions can be used to query the pointers to the actual types.
    // I made them return a null pointer if the wrong getter was used.
    A *getA() {
        return m_which == A_Type ? m_ptr.a : 0;
    }
    B *getB() {
        return m_which == B_Type ? m_ptr.b : 0;
    }
}

请注意,如果您复制 EitherAorB 的实例,此实现将缺少内存.要解决此问题,请禁用复制(通过将复制构造函数和赋值运算符设为私有(private)或在 C++11 中使用 = delete 禁用它们),或者实现将深度复制指针对象的复制构造函数和赋值运算符。


你说你不熟悉模板编程。使这个实现模板化并不困难。就放template<typename A, typename B>在整个类定义之前;然后它应该开箱即用。但是,不要移动 .cpp 中的实现在这种情况下的文件;最好是让它们在我写的时候保持内联。

然后,AB不是类型,而是您在客户端代码中分配类型的占位符。然后我将 tempalte 类重命名为 Either , 所以你的类型名称变成类似 Either<This, That> 的东西.

关于c++ - 如何在 C++ 中实现带有变体字段的简单类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16479965/

相关文章:

arrays - 如果匹配多个相同的数据,如何循环?

delphi - 从 Variant String 转换为 Double 忽略小数点

c++ - 为什么 vulkan 在集成显卡和 GPU 的系统中报告单个设备?

c++ - 如何指定析构函数调用的顺序?

c++ - 内联函数和复制构造函数

c++ - 是否存在将 double 转换为 long long 的优雅方法?

c++ - 我可以将一个类与模板中的另一个类关联起来(使用 C++17 变体)吗?

c++ - 用对 vector 插入

c# - 通过引用从 c# COM Visible 类中的 c# 方法返回 vb 变体

winapi - 如何通过 IDispatch 将 SAFEARRAY 传递给 COM 对象?