一个数组,多种类型
One array, multiple types
我有一个数组,它是 FIFO 列表,我正在向其中添加数据记录。
我的数据记录可以是任何标准类型(string、char、int、long、unsigned、float、double),我使用函数将其推送到我的数组。
我想稍后按添加的顺序读取该数据记录。
这是我试过的:
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(...);
....
由于您只能使用标准库,因此您需要自己实施解决方案或使用现有的易于适应的解决方案。您要查找的内容称为 tagged union 或变体。这是一个数据结构,其中包含一个 union
以在同一位置保存多种类型的数据 和 联合之外的单独值以指示哪个元素是 active/used.
对于整数类型,这是相当容易管理的,因为您只需要为设置和检索值提供原始支持。对于 std::string
和 std::vector
等更复杂的类型,如果您不使用 C++11,事情会变得有点困难。这是因为在 C++11 之前,union
不能包含具有非平凡复制构造函数、非平凡析构函数或非平凡复制赋值运算符的类型。
评论(here and here)中提供的两个示例any
类似乎是合理且完整的实现,但需要C++11。两者都仅依赖于标准库中的组件,因此它们可能是适合您的可行解决方案。但是,如果您不使用 C++11 或者需要一个更简单的解决方案来作为您的解决方案的基础,我在下面提供了一个示例。它处理 char
、int
和 double
类型,因此如果您需要支持更复杂的类型,则需要添加它们。对于复杂类型(并且没有 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,它定义了你需要对值进行的所有操作,然后为每种类型的值定义一个派生class:
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"));
需要注意的一点是容器中包含指针,并且值是动态分配的。这意味着当容器被销毁时,这些值不会自动释放。所以你将不得不在容器被销毁时手动删除元素,或者使用某种形式的智能指针而不是裸指针作为存储在容器中的类型。
主要好处是您没有一个 class 可以处理所有可能的类型。相反,它们很好地隔离了。例如,您可以通过简单地实现新的 class 来添加对新类型的支持,而无需更改任何现有代码。
我有一个数组,它是 FIFO 列表,我正在向其中添加数据记录。
我的数据记录可以是任何标准类型(string、char、int、long、unsigned、float、double),我使用函数将其推送到我的数组。
我想稍后按添加的顺序读取该数据记录。
这是我试过的:
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(...);
....
由于您只能使用标准库,因此您需要自己实施解决方案或使用现有的易于适应的解决方案。您要查找的内容称为 tagged union 或变体。这是一个数据结构,其中包含一个 union
以在同一位置保存多种类型的数据 和 联合之外的单独值以指示哪个元素是 active/used.
对于整数类型,这是相当容易管理的,因为您只需要为设置和检索值提供原始支持。对于 std::string
和 std::vector
等更复杂的类型,如果您不使用 C++11,事情会变得有点困难。这是因为在 C++11 之前,union
不能包含具有非平凡复制构造函数、非平凡析构函数或非平凡复制赋值运算符的类型。
评论(here and here)中提供的两个示例any
类似乎是合理且完整的实现,但需要C++11。两者都仅依赖于标准库中的组件,因此它们可能是适合您的可行解决方案。但是,如果您不使用 C++11 或者需要一个更简单的解决方案来作为您的解决方案的基础,我在下面提供了一个示例。它处理 char
、int
和 double
类型,因此如果您需要支持更复杂的类型,则需要添加它们。对于复杂类型(并且没有 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,它定义了你需要对值进行的所有操作,然后为每种类型的值定义一个派生class:
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"));
需要注意的一点是容器中包含指针,并且值是动态分配的。这意味着当容器被销毁时,这些值不会自动释放。所以你将不得不在容器被销毁时手动删除元素,或者使用某种形式的智能指针而不是裸指针作为存储在容器中的类型。
主要好处是您没有一个 class 可以处理所有可能的类型。相反,它们很好地隔离了。例如,您可以通过简单地实现新的 class 来添加对新类型的支持,而无需更改任何现有代码。