11

ライブラリを使用せずにC++でシリアル化/逆シリアル化がどのように機能するかを理解しようとしています。単純なオブジェクトから始めましたが、ベクトルを逆シリアル化すると、最初にサイズを記述しないとベクトルを取得できないことがわかりました。さらに、ベクトルのサイズより前に数字が存在する場合、それを正しく読み取ることができないため、どのファイル形式を選択すべきかわかりません。さらに、クラスとマップコンテナでそれを実行したいと思います。私の仕事は、次のようなオブジェクトをシリアル化/逆シリアル化することです。

PersonInfo
{
    unsigned int    age_;
    string name_;
    enum { undef, man, woman } sex_;
}

Person : PersonInfo 
{
    vector<Person>      children_;
    map<string, PersonInfo>     addrBook_;
}

現在、私は次のような単純なオブジェクトをシリアル化する方法を知っています。

vector<PersonInfo> vecPersonInfo;
vecPersonInfo.push_back(*personInfo);
vecPersonInfo.push_back(*oneMorePersonInfo);

ofstream file("file", ios::out | ios::binary);
if (!file) {
    cout<<"can not open file";
} else {
    vector<PersonInfo>::const_iterator iterator = vecPersonInfo.begin();
    for (; iterator != vecPersonInfo.end(); iterator++) {
        file<<*iterator;
    }

この複雑なオブジェクトまたはそれを明確に説明する優れたチュートリアルに対して、これをどのように行うことができますか?

4

2 に答える 2

14

1つのパターンは、抽象クラスを実装することです。これは、シリアル化の関数を定義し、クラスは、シリアライザーに何が入り、何が出るかを定義します。例は次のとおりです。

class Serializable
{
public:
    Serializable(){}
    virtual ~Serializable(){}

    virtual void serialize(std::ostream& stream) = 0;
    virtual void deserialize(std::istream& stream) = 0;
};

次に、シリアル化するクラス/構造体のSerializableインターフェースを実装します。

struct PersonInfo : public Serializable // Yes! It's possible
{
    unsigned int age_;
    string name_;
    enum { undef, man, woman } sex_;

    virtual void serialize(std::ostream& stream)
    {
        // Serialization code
        stream << age_ << name_ << sex_;
    }

    virtual void deserialize(std::istream& stream)
    {
        // Deserialization code
        stream >> age_ >> name_ >> sex_;
    }
};

残り私はあなたが知っていると信じています。ここにいくつかのハードルがあり、余暇に行うことができます。

  1. スペースを含む文字列をストリームに書き込んで読み戻そうとすると、その一部のみが取得され、残りの文字列はその後に読み取られた値を「破損」します。
  2. クロスプラットフォーム(リトルエンディアンとビッグエンディアン)になるようにプログラムするにはどうすればよいですか?
  3. プログラムは、逆シリアル化するときに作成するクラスをどのように自動的に検出できますか。

手がかり:

  1. bool、int、float、stringsなどを書き込む関数を持つカスタムシリアライザーを使用します。
  2. 文字列を使用してシリアル化されるオブジェクトタイプを表し、ファクトリを使用して逆シリアル化時にそのオブジェクトのインスタンスを作成します。
  3. 事前定義されたマクロを使用して、コードがコンパイルされているプラ​​ットフォームを判別します。
  4. 常に固定エンディアンでファイルを書き込み、他のエンディアンを使用するプラットフォームをそれに合わせて調整します。
于 2012-07-10T15:12:24.267 に答える
2

最も基本的な形式は、仮想読み取り/書き込みメソッドを定義する「Serialisable」インターフェース(抽象クラ​​ス)を定義することです。また、基本的なプリミティブ型(int、float、bytes、chars、seek / resetなど)および一部の複合型(文字列、ベクトルなどの値の配列)に共通のAPIを提供する「Stream」インターフェースを定義します。 、など)ストリーム上で動作します。必要に応じて、C++IOStreamsを使用できます。

また、ファクトリがロード/デシリアライズ時に対応するクラスを作成し、必要に応じて各論理パーツが適切な構造/長さ情報でタグ付け/ヘッダーされるように複合型をシリアル化するときに参照するためのIDシステムが必要になります。

次に、メディアごとに具体的なStreamクラスを作成できます(テキストファイル、バイナリファイル、メモリ内、ネットワークなど)。

シリアライズ可能にしたい各クラスは、シリアライズ可能インターフェースを継承し、詳細を実装する必要があります(複合/複合クラスの場合、他のタイプ用に定義されたシリアライズ可能インターフェースを再帰的に活用します)。

もちろん、これはシリアル化を追加するための単純で「煩わしい」方法です(参加しているクラスを変更する必要があります)。次に、テンプレートまたはプリプロセッサのトリックを使用して、邪魔にならないようにすることができます。これがコードでどのように見えるかについてのアイデアについては、Boostまたはプロトコルバッファ、またはその他のライブラリを参照してください。

あなたは本当にあなたがあなた自身を転がしたいのですか?特にポインタ、オブジェクト間のポインタ(サイクルを含む)がある場合は、非常に厄介になる可能性があります。これは、ロード/逆シリアル化が現在の実行に対して正しい前に、ある時点で修正/変換する必要もあります。

于 2012-07-10T15:12:53.647 に答える