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

How to implement simple class with variant field in c++

本文关键字:字段 变量 简单 c++ 实现      更新时间:2023-10-16

我想实现一个类假设它有字段键和类A或B。这个类的构造函数的参数是字符数组。构造函数伪代码会查看第一个字符,如果它与0x00有性,它将创建a类对象,否则它将创建类B对象-两个类都将以字符数组作为参数。

无论如何,我想保持这个实现简单。除非我真的需要,否则我不想使用boost::Variant,我也不想实现这样的东西实现一个"变种"类因为我不熟悉模板编程,我认为我的问题可以用更简单的方式实现。

对于POD类型,我们有union(但是联合不会记住您分配的类型,所以也要单独存储)。这对非pod类型不起作用。主要原因是c++不知道在构造时应该创建哪个,在删除联合时应该删除哪个。

但是联合可以用来保存指向实际类型的指针。然后你必须自己关心构造和删除。

您可以创建类似这样的东西,它包装了这个指针联合并添加了一个方便的接口。详细的解释写在注释中:

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的实例,该实现将缺少内存。要解决这个问题,要么禁用复制(通过将复制构造函数和赋值操作符设置为私有,或者在c++ 11中使用= delete禁用它们),要么实现将深度复制指针的复制构造函数和赋值操作符。


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

那么,AB不是类型,而是您在客户端代码中分配类型的占位符。然后我将模板类重命名为Either,这样类型名称就变成了类似Either<This, That>的东西。