一个数组,多种类型

One array, multiple types

本文关键字:种类 类型 数组 一个      更新时间:2023-10-16

我有一个数组,它是FIFO列表,我正在将数据记录添加到其中。
我的数据记录可以是任何标准类型(字符串、字符、整数、长整型、无符号、浮点数、双精度),我使用函数将其推送到我的数组。
我想稍后按添加顺序读取该数据记录。

这是我尝试过的:

class List
{
    typedef std::pair<typename, std::vector<char>> Record; // typename ??
    public:
        template <typename T>
        void addRecord(T value)
        {
            char* arr = reinterpred_cast<char*>(&value);  // Casting ?
            // Convert to Record and push to _records
        }
        template <typename T>
        T getRecord(std::vector<Record>::iterator record) const
        {
            // Convert Record to T and return 
        }
    private:
        std::vector<Record> _records;
}

如何从这些类型转换为字节数组,或者还有其他方法可以做到这一点吗?

我想使用它的示例方式:

List list;
list.addRecord("Test string");
list.addRecord(10);
list.addRecord(999999);
list.addRecord("Test string 2");
list.addRecord('X');
...

然后以相同的方式阅读它们:

std::string testString = list.getRecord(...);
char testChar = list.getRecord(...);
int testInt = list.getRecord(...);
....

由于您只能使用标准库,因此您需要自己实现解决方案或使用现有的易于适应的解决方案。您要查找的内容称为标记联合或变体。这是一种数据结构,其中包含一个在同一位置保存多种类型数据的union,以及一个联合外部的单独值,用于指示哪个元素处于活动状态/已使用状态。

对于整型类型,这是相当合理的,因为您只需要为设置和检索值提供原始支持。对于更复杂的类型,如std::stringstd::vector如果您不使用 C++11,事情会变得更加困难。这是因为在 C++11 之前,union不能包含具有非平凡复制构造函数、非平凡析构函数或非平凡复制赋值运算符的类型。

注释(此处和此处)中提供的示例any类似乎是合理且完整的实现,但需要 C++11。两者都仅依赖于标准库中的组件,因此它们可能是可行的解决方案。但是,如果您不使用 C++11 或需要更简单的解决方案来基于您的解决方案,我在下面提供了一个示例。它处理charintdouble类型,所以如果你需要支持更复杂的类型,你需要添加它们。对于复杂类型(没有 C++11),您需要持有指向实例的指针并自行管理生存期(手动或使用智能指针)。您还需要根据您的特定需求(深拷贝与浅拷贝)处理复制和分配。

简单标记联合:

struct TaggedUnion
{
    enum Type
    {
        Char,
        Int,
        Double
    };

    TaggedUnion(const char& value) : type(Char), value(value) {}
    TaggedUnion(const int& value) : type(Int), value(value) {}
    TaggedUnion(const double& value) : type(Double), value(value) {}
    Type getType() const { return type; }
    char getChar() const { assert(type == Char); return value.getChar(); }
    int getInt() const { assert(type == Int);  return value.getInt(); }
    double getDouble() const { assert(type == Double);  return value.getDouble(); }

private:
    union Union
    {
        Union(const char& value) : charValue(value) {}
        Union(const int& value) : intValue(value) {}
        Union(const double& value) : doubleValue(value) {}
        char getChar() const { return charValue; }
        int getInt() const { return intValue; }
        double getDouble() const { return doubleValue; }
    private:
        char    charValue;
        int     intValue;
        double  doubleValue;
    };
    Type type;
    Union value;
};

用法示例:

#include <iostream>
#include <vector>
#include <cassert>
int main()
{
    std::vector<TaggedUnion> values;
    values.push_back(TaggedUnion(0.0));    //  Store double/float
    values.push_back(TaggedUnion(0));       //  Store int
    values.push_back(TaggedUnion(' '));     //  Store char
}

使用联合,就像另一个答案中建议的那样,是最直接的解决方案。作为替代方案,我想提出一种我认为更加面向对象的方法。这个想法是为值定义一个抽象基类,该基类定义值所需的所有操作,然后为每种类型的值定义一个派生类:

class Value {
public:
    virtual ~Value() {}
    // Define operations needed for values, e.g.
    virtual void print() = 0;
};
class IntValue: public Value {
public:
    IntValue(int val) : m_val(val) {}
    virtual void print() {
        std::cout << m_val;
    }
private:
    int m_val;
};
class StringValue: public Value {
public:
    StringValue(const std::string& val) : m_val(val) {}
    virtual void print() {
        std::cout << m_val;
    }
private:
    std::string m_val;
};
// Equivalent class definitions for other value types.

然后,要构建值集合,请执行以下操作:

std::vector<Value*> values;
values.push_back(new IntValue(42));
values.push_back(new StringValue("Hello"));

需要注意的一件事是容器包含指针,并且值是动态分配的。这意味着在销毁容器时不会自动释放这些值。因此,您必须在容器被销毁时手动删除元素,或者使用某种形式的智能指针而不是裸指针作为存储在容器中的类型。

主要好处是你没有一个处理所有可能类型的类。相反,它们被很好地隔离了。例如,可以通过简单地实现新类来添加对新类型的支持,而无需更改任何现有代码。