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>は完全に異なる2つのタイプです。それらをベクターで処理するには、どこかでジェネラライズする必要があります。あなたは書くことができ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し、に少なくとも1つの純粋な仮想を書き込みAbstractFieldます。

AbstractField純粋な仮想のデストラクタを作成できます。そして、すべてのを削除することを忘れないでくださいAbstractField。の~Database_Record()

于 2012-08-17T16:20:26.243 に答える
1

あなたは間違った方向に進んでいます。

テンプレートは、異なるタイプを作成するために使用されます。std::vector<int>およびstd::vector<float>は、およびとほぼ同じように(そして同じように)区別されintますfloat

構文も間違っています。動的配列を作成するには、次のメンバーを次のように配置しますDatabase_Record

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

異なるタイプの複数のオブジェクトを単一の配列に入れるには、それらに共通の基本クラスが必要です。

于 2012-08-17T16:15:35.437 に答える
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

前に述べたように、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[]これで、必要に応じて、1回の呼び出しで特定のタイプのレコードの配列としてテーブルを割り当てることができます。また、構造全体のメモリを解放するため、通常は各フィールドの破棄について気にする必要はありません。

マクロは、そのような特殊化の宣言をいくらかコンパクトにするのに役立ちます。

于 2012-08-17T17:00:54.133 に答える
0

テンプレートを間違って実行しています。異なるタイプのクラステンプレートをインスタンス化すると、サイズが異なる可能性のある2つの異なるタイプが再び生成され、配列に格納できなくなります。

異なるタイプを均一に扱いたい場合は、継承を使用してください。また、継承を使用する場合は、プレーン配列を使用しないでください。vectorまたは 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"}のようになります。

ベースは意図的に純粋な仮想ではありません-必要なクロスゲッターは意味がありません...

于 2021-02-07T10:48:05.207 に答える