4

基本クラスのポインターを介して派生クラスのメンバーにアクセスする一般的なコードを持つ方法はありますか? または、これを回避する他の方法はありますか?

クラス Shape があるとしましょう。それを継承するクラス Square と Triangle があります。両方とも、互いに何の関係もない独自のプライベート メンバーを持っているため、それらを基本クラスに含める意味はありません。では、クラスをファイルに書き込む必要があるときに、ファイルに書き込む必要がある瞬間までクラスが Square なのか Triangle なのかがわからない場合はどうすればよいでしょうか?

私はこの問題を解決する方法を見つけようとしています。最悪の場合の解決策は、Square AND Triangle の両方のデータをファイルに書き込み、読み取りと書き込みの両方に識別子 (Triangle または Square) を追加し、データをロードするときに小さなパーサーにクラスをまとめさせることです。これは非効率であり、時間の無駄です。

トリックやデザインパターン、または状況に役立つ何かがあるかどうか疑問に思っていました.

4

5 に答える 5

8

このシリアル化は、仮想関数を使用して行う必要があります。オブジェクトをシリアル化する基本クラスで関数を定義します。三角形と四角形はこの関数をオーバーライドし、次のように記述します。

  • 識別子
  • シリアル化する必要があるすべてのデータ

必要に応じて、基本クラスに共通部分を実装できます。

ファイルをロードする場合は、識別子に対応するクラス インスタンスを作成するファクトリ メソッドが必要になります。実際のデータをロードするには、新しいインスタンスの仮想デシリアライズ メソッドを呼び出す必要があります。

于 2012-09-04T14:13:52.823 に答える
3

基本クラスに純粋な仮想ゲッターを含めることができます。そして、すべての派生クラスがそれをオーバーライドします。このような

class Shape{
  public:
    virtual int data() const = 0;
};
class Square: public Shape{
  private:
     int _member;
  public:
    virtual int data() const{
     //do something with private members and return it
     return _member;
    };
};
于 2012-09-04T14:12:06.167 に答える
1

このオーバーヘッドを取り除く直接的な方法はないと思います。通常、これは 2 つのことによって行われます。まず、オブジェクトにはシリアル化メカニズムが必要です。

物事をシリアライズするには、シリアライズ先の場所が必要です。この場合、データ コンテナーを使用してこれを行いますが、これはファイル ストリームまたはコンテナー クラスの場合もあります。シリアル化はオブジェクト内または外部から行うことができます。最も簡単な実装は内部から行うことです。

単純なシリアル化部分:

class Shape{
public:
  virtual void serialize( Datacontainer &o ) const = 0;
};

class Triangle: public Shape{
  void serialize( Datacontainer &o ) const{
    o.add('T');
    o.add(corners);
  }
  std::vector<point> corners;
}

class Circle: public Shape{
  void serialize( Datacontainer &o ) const{
    o.add('C');
    o.add(center);
    o.add(radius);
  }
  point center;
  double radius;
}

シリアル化中に、基本クラス Shape を使用してこれを行うことができます。

Shape *tri = new Triangle;
tri->serialize( dataContainer or file );

型を知る必要があるため、逆シリアル化はそれほど簡単ではありません。これに適したパターンはBuilder パターンです。それにもかかわらず、これを行うためにより C++ らしい方法を実装できます。

すべてのクラスに次のものを追加します。

static Shape* createFrom( Datacontainer &o );

たとえば。サークルの実装:

Shape* Circle::createFrom( Datacontainer &o )
{
  Circle *c = new Circle()
  c->center = o.get();
  c->radius = o.get();
}

これにより、具体的なインスタンスを作成できますが、メソッドには共通の関数フットプリントがあります。これで、次のような非常に簡単なビルダーを実装できます。

class ShapeBuilder
{
public:
  static Shape* createShape( Datacontainer& o )
  {
    char id = o.get();
    swith(id){
    case 'T':
      return Triangle::createFrom(o);
    case 'C':
      return Circle::createFrom(o);
    }        
  }
}
于 2012-09-04T15:51:46.607 に答える
0

おそらく最良の方法は、このようなことをすることです。基本的なパターンは、すべての派生クラスで常に同じであることが保証されている共通コードをベースに配置できることです。異なる必要があるものは、派生クラスがそれぞれ異なる方法で実装する仮想関数に入れます。

class Shape {
    virtual void writeSerialData(std::ostream &) const = 0;
  public:
    void writeToFile(const std::string &filename) const {
        std::ofstream outfile(filename); // filename.c_str() in C++03
        writeSerialData(outfile);
        if (!outfile.close()) {
            // report the error
        }
    }
    virtual ~Shape() {}
};

class Square : public Shape {
    double length;
    virtual void writeSerialData(std::ostream &out) const {
        out << "Square{" << length << '}';
    }
  public:
    Square(double l) : length(l) {}
};

次の問題があります。オブジェクトがどの派生クラスであるかを事前に知らずに、ファイルからオブジェクトをどのように読み戻すのでしょうか。そのためには、テキストを表示し、(a)データの解釈方法を知っているSquareクラスの静的関数を呼び出すかSquare、(b)解釈するデータを与えることによってクラスSquareをインスタンス化する方法が必要です。その道を行き過ぎる前に、BoostSerializationまたは他のシリアル化ライブラリを調べる価値があります。

于 2012-09-04T14:28:47.950 に答える
0

基本クラスで仮想メソッドを宣言し、派生クラスでそれらを定義する必要があります。ただし、それらをファイルに保存する場合は、メモリレイアウトが異なる可能性があるため、ファイル内の特定のクラスインスタンスを識別する方法が必要になります。

于 2012-09-04T14:11:36.563 に答える