我已经很长时间没有完成任何C ++编程了,我决定在闲暇时间把它搞得一团糟,所以我决定给我写一个小数据库程序只是为了好玩而我#&# 39; m在创建模板化类对象数组时遇到问题。
我所拥有的是这个类,我想用它来表示数据库记录中的字段。
template <class T, int fieldTypeId>
class Field
{
private:
T field;
int field_type;
public:
// ...
};
我想使用该类的数组来表示使用此类的数据库中的记录。
class Database_Record
{
private:
int id;
Field record[];
public:
Database_Record(int);
Database_Record(int, Field[]);
~Database_Record();
};
我坚持的地方是在Database_Record
类中创建数组,因为这是一个模板化类对象的数组,每个元素可能是不同类型的,我不是确定我需要如何声明数组。是我试图做甚至可能的事情还是我错误的做法?任何帮助将不胜感激。
答案 0 :(得分:14)
Field<T1>
和Field<T2>
是两种完全不同的类型。要在矢量中处理它们,您需要在某处进行序列化。您可以写AbstractField
和
struct AbstractField{
virtual ~AbstractField() = 0;
};
template<class T,int fieldTypeId>
class Field: public AbstractField{
private:
T field;
public:
const static int field_type;
public:
virtual ~Field(){}
};
class Database_Record{
std::vector<AbstractField*> record;
public:
~Database_Record(){
//delete all AbstractFields in vector
}
};
然后保留vector
AbstractField
。也可以使用vector
代替[]
。使用AbstractField*
代替AbstractField
,并在AbstractField
中至少编写一个纯虚拟文字。
你可以使AbstractField
纯粹的析构函数虚拟化。并且不要忘记删除所有AbstractField
。在~Database_Record()
答案 1 :(得分:1)
你走错了路。
模板用于创建不同的类型:std::vector<int>
和std::vector<float>
的区别与int
和float
的方式大致相同(和同样多)。
你的语法也错了;要创建动态数组,您需要将以下成员添加到Database_Record
:
std::vector<Field> record; // if this was possible; however, it's not
要将几个不同类型的对象放入一个数组中,它们应该有一个共同的基类。
答案 2 :(得分:1)
为了创建不同类型的数组,您需要一个对象的基类,该数组将是指向该基类的指针数组。所以,例如,
class Field
{
public:
virtual ~Field() {}
virtual std::string toString() const = 0;
// and possibly other interface functions...
};
template <class T> FieldImpl : public Field
{
public:
virtual std::string toString() const
{
std::stringstream ss;
ss << val;
return ss.str();
}
// implementation of possibly other interface functions
private:
T val;
}
将是您需要的类型。那么数组就像
std::vector<std::unique_ptr<Field>> my_array;
然后,您可以使用接口函数对数组执行操作,例如: G。
my_array[i]->toString();
答案 3 :(得分:0)
你做的模板错了。使用实例化类模板 不同的类型可能会再次产生两种不同的类型 不同的大小,这使得它们无法存储在数组中。
如果要统一处理不同类型,请使用继承。和
当你使用继承时,不要使用普通数组,而是使用vector
或
std::array
。
你的代码中还有一些奇怪的东西:为什么存储一个
fieldTypeId
什么时候静态知道?我想这与它有关
您用作模板参数的类型T
。外化了
通过部分专业化的机制:
template<typename T>
struct fieldTypeId;
template<>
struct fieldTypeId<int> {
const static int value = 0;
};
// etc....
如果我完全错了,你真的知道你在做什么:使用
通过某种any
类型输入类型(例如Boost.Any)。
答案 4 :(得分:0)
将每个使用不同模板参数的实例化视为不同的类。您需要存储特定的类(即Field<int, 17>
),或者需要Field
来创建一个非模板化的基类,您可以 存储在列表中。
答案 5 :(得分:0)
你可以这样做 -
template <class T, int fieldTypeId>
class Field
{
private:
T field;
int field_Type;
};
template <class T, int fieldTypeId>
class Database_record
{
private:
int id;
std::vector<Field<T, fieldTypeId> > record_;
};
答案 6 :(得分:0)
如前所述,C ++模板不能像那样工作。
同样,由于性能限制,使用继承和指针向量不适合DB记录的实现。
退后一步,以更抽象的方式看待问题。正如我从您的代码中理解的那样,目的是将任意数量的不同类型的字段打包到连续的内存块中。示意性地:
struct DBRecord {
Type1 f1;
Type2 f2;
Type3 f3;
Type4 f4;
// etc...
}
你可以通过一个有点丑陋但实用的构造来实现这一点,该构造由抽象模板声明和几个特化组成。
声明如下:
template <
typename T1,
typename T2 = void,
typename T3 = void,
typename T4 = void,
typename T5 = void,
typename T6 = void,
typename T7 = void,
typename T8 = void,
typename T9 = void,
typename T10 = void
> struct DBRecord;
它明显地将最大字段数限制为某个特定数字。如果您需要真正任意数量的字段,则需要切换到面向列的范例。
然后,部分专业化应该声明从1到10的每个参数数量的结构解剖:
template <
typename T1
> struct DBRecord <T1, void, void, void, void, void, void, void, void, void>
{
int id;
T1 f1;
DBRecord(int ID, T1 F1) {/*...*/};
};
template <
typename T1,
typename T2
> struct DBRecord <T1, T2, void, void, void, void, void, void, void, void>
{
int id;
T1 f1;
T2 f2;
DBRecord(int ID, T1 F1, T2 F2) {/*...*/};
};
// etc...
现在,如果需要,您可以在一个new[]
调用中将表分配为某些类型的记录数组。
而且,你通常不关心每个领域的破坏,因为你可以释放整个结构的记忆。
宏可以帮助使这种专业化的声明更加紧凑。
答案 7 :(得分:0)
受 C# 的 ToString() 覆盖启发,制作了 2 个用于快速调试报告的示例类:
class UnknownType_t {
public:
virtual operator long&() { throw "Unsupported"; };
virtual operator const std::string() { throw "Unsupported"; };
virtual void Set(char*, long) = 0;
};
class Number : public UnknownType_t {
public:
Number(long _n) { n = _n; };
virtual operator long&() { return n; };
virtual void Set(char* buf, long size) {
n = 0;
memcpy(&n, buf, size);
}
long n;
};
class String : public UnknownType_t {
public:
String(const char *_s) : s(_s) {};
virtual operator const std::string() { return s; };
virtual void Set(char* buf, long size) {
s = std::string(reinterpret_cast<char*>(buf), size);
}
std::string s;
};
您可以尝试使用 dynamic_cast 检查类型,结果是 UnknownType_t 的公共数组看起来像 {n=123 } 或 {s="ABC" }。
Base 不是纯虚拟的 - 所需的交叉 getter 没有意义......