12

我已经有一段时间没有做过任何 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类中创建数组,因为这是一个模板类对象数组,每个元素可能属于不同的类型,因此我不确定如何声明数组。我正在尝试做的事情是可能的,还是我做错了?任何帮助将不胜感激。

4

8 回答 8

18

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

然后保持一个vectorAbstractField也使用vector代替[]. 使用AbstractField*代替AbstractField并在AbstractField.

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

于 2012-08-17T16:20:26.243 回答
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;

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

my_array[i]->toString();
于 2012-08-17T16:26:26.357 回答
1

你走错路了。

模板用于创建不同的类型:std::vector<int>并且以与以往相同的方式(和std::vector<float>一样多)不同。intfloat

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

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

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

于 2012-08-17T16:15:35.437 回答
1

如前所述,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[]现在,如果需要,您可以在一次调用中将表分配为特定类型的记录数组。而且,您通常不关心每个字段的破坏,因为您释放了整个结构的内存。

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

于 2012-08-17T17:00:54.133 回答
0

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

如果要统一处理不同的类型,请使用继承。并且当你使用继承时,不要使用普通数组,而是使用vectoror std::array

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

template<typename T>
struct fieldTypeId;

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

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

于 2012-08-17T16:16:53.857 回答
0

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

于 2012-08-17T16:17:00.940 回答
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_;
};
于 2012-08-17T16:20:16.087 回答
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 并非纯虚拟的意图 - 所需的交叉吸气剂没有意义......

于 2021-02-07T10:48:05.207 回答