如何创建模板化类对象的数组?

时间:2012-08-17 16:09:41

标签: c++ arrays templates

我已经很长时间没有完成任何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类中创建数组,因为这是一个模板化类对象的数组,每个元素可能是不同类型的,我不是确定我需要如何声明数组。是我试图做甚至可能的事情还是我错误的做法?任何帮助将不胜感激。

8 个答案:

答案 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>的区别与intfloat的方式大致相同(和同样多)。

你的语法也错了;要创建动态数组,您需要将以下成员添加到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)

你做的模板错了。使用实例化类模板 不同的类型可能会再次产生两种不同的类型 不同的大小,这使得它们无法存储在数组中。

如果要统一处理不同类型,请使用继承。和 当你使用继承时,不要使用普通数组,而是使用vectorstd::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 没有意义......