繁体   English   中英

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

[英]How to create an array of templated class objects?

我已经有很长一段时间没有做过任何 C++ 编程了,我决定在我的业余时间稍微弄乱它,所以我决定写一个小数据库程序只是为了好玩,但我在创建时遇到了麻烦一组模板化的类对象。

我拥有的是这个类,我想用它来表示数据库记录中的一个字段。

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类中创建数组,因为这是一个模板化类对象的数组,每个元素可能是不同的类型,因此我不确定我需要如何声明数组。 我正在尝试做的事情是可能的还是我以错误的方式去做? 任何帮助将不胜感激。

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
    }
};

然后保留一个AbstractField vector 也使用vector而不是[] 使用AbstractField*代替AbstractField和至少一个写纯虚在AbstractField

您可以使AbstractField的析构函数纯虚拟。 并且不要忘记删除所有AbstractField ~Database_Record()

你走错路了。

模板用于创建不同的类型: std::vector<int>std::vector<float>的区别与intfloat区别方式(和同样多)非常相似。

你的语法也是错误的; 要创建一个动态数组,您需要将以下成员放入Database_Record

 std::vector<Field> record; // if this was possible; however, it's not

要将几个不同类型的对象放入一个数组中,它们应该有一个共同的基类。

为了创建不同类型的数组,您需要一个对象的基类,该数组将是指向该基类的指针数组。 所以,例如,

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;

然后,您可以使用接口函数对数组进行处理,例如

my_array[i]->toString();

如前所述,C++ 模板不是那样工作的。

同时,由于性能限制,使用继承和指针向量不适合数据库记录的实现。

退后一步,以更抽象的方式看待问题。 正如我从您的代码中了解到的,目的是将任意数量的不同类型的字段打包到一个连续的内存块中。 示意图:

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[]调用中将表分配为某些类型的记录数组。 而且,您通常不关心每个字段的销毁,因为您释放了整个结构的内存。

宏可以帮助使此类特化的声明更加紧凑。

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

如果要统一对待不同类型,请使用继承。 当您使用继承时,不要使用普通数组,而是使用vectorstd::array

您的代码中还有一堆奇怪的东西:为什么在静态已知的情况下存储fieldTypeId 我猜这与您用作模板参数的类型T有关。 通过部分专业化将机制外部化:

template<typename T>
struct fieldTypeId;

template<>
struct fieldTypeId<int> {
  const static int value = 0;
}; 
// etc....

如果我完全错了并且你真的知道你在做什么:通过一些any类型(例如Boost.Any )使用类型擦除。

将具有不同模板参数的每个实例化视为不同的类。 您要么需要存储一个特定的类(即Field<int, 17> ),要么需要Field有一个可以存储在列表中的非模板化基类。

你可以做这样的事情 -

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_;
};

受 ToString() 覆盖 C# 的启发,制作了 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 不是纯粹的虚拟意图 - 所需的交叉吸气剂将毫无意义......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM