1

プロジェクトにyaml-cppを使用しています。<<一部のクラスのand演算子をオーバーロードしたいの>>ですが、これを「適切に」行う方法に取り組んでいます。Noteたとえば、クラスを取ります。それはかなり退屈です:

class Note {
  public:
    // constructors
    Note( void );
    ~Note( void );

    // public accessor methods
    void            number( const unsigned long& number ) { _number = number; }
    unsigned long   number( void ) const                  { return _number; }
    void            author( const unsigned long& author ) { _author = author; }
    unsigned long   author( void ) const                  { return _author; }
    void            subject( const std::string& subject ) { _subject = subject; }
    std::string     subject( void ) const                 { return _subject; }
    void            body( const std::string& body )       { _body = body; }
    std::string     body( void ) const                    { return _body; }

  private:
    unsigned long   _number;
    unsigned long   _author;
    std::string     _subject;
    std::string     _body;
};

<<オペレーターは簡単なソースです。で.h

YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v );

そしてで.cpp

YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v ) {
  out << v.number() << v.author() << v.subject() << v.body();
  return out;
}

汗かいていない。次に、>>演算子を宣言します。で.h

void operator >> ( const YAML::Node& node, Note& note );

しかし、.cpp私は得る:

void operator >> ( const YAML::Node& node, Note& note ) {
  node[0] >> ?
  node[1] >> ?
  node[2] >> ?
  node[3] >> ?
  return;
}

もし私がそのようなことを書いたらnode[0] >> v._number;、CV 修飾子を変更してすべてのNoteフィールドを作成する必要がありますpublic(これは、データの隠蔽について (教授、本、および経験によって) 教えられたすべてのものを打ち負かします)。

node[0] >> temp0; v.number( temp0 );いたるところで行うのは、退屈で、エラーが発生しやすく、見苦しいだけでなく、無駄が多いように感じます (余分なコピーの場合)。

それから私は賢明になりました: 私はこれらの 2 つの演算子をNoteクラス自体に移動し、それらを s として宣言しようとしましfriendたが、コンパイラ (GCC 4.4) はそれを好きではありませんでした:

src/note.h:44: エラー: 'YAML::Emitter& Note::operator<<(YAML::Emitter&, const Note&)' は 1 つの引数を取る必要があります
src/note.h:45: エラー: 'void 注: :operator>>(const YAML::Node&, Note&)' は引数を 1 つだけ取る必要があります

質問:>>クラスの演算子を「適切に」オーバーロードするにはどうすればよいですか

  1. 情報隠蔽の原則に違反することなく?
  2. 過度のコピーなしで?
4

5 に答える 5

3

カプセル化に違反せずにこれを行う一般的な方法は、演算子>>をフレンド関数にすることです。フレンド演算子の宣言に構文上の問題があったに違いありません(エラーメッセージから正確に何を明確にしないでください)。私はYAMLを使用していませんが、あなたの質問から、次のことがその要点です。

class Note{
    ...
    friend void operator >> ( const YAML::Node& node, Note& note );
    ....
 };
 void operator >> ( const YAML::Node& node, Note& note ){
    node[0] >> note._number;
    node[1] >> note._author;
    node[2] >> note._subject;
    node[3] >> note._body;
 }

フレンド関数には、メンバー関数と同じプライベートメンバーへのアクセス権があります。

または、すべてのメンバーデータに対してセッターを宣言することもできますが、friend関数メソッドの方がクリーンです。

于 2010-06-07T03:54:41.673 に答える
3

ヘルパーメソッドを使用するのが好きです。メソッドはクラスの一部であるため、すべてのプライベート フィールドに完全にアクセスできます。

class Note {
public:
    void read(const YAML::Node& node)
    {
        node >> ...;
    }
};

そしてoperator>>、通話を転送するだけです:

const YAML::Node &operator >> ( const YAML::Node& node, Note& note ) {
    note.read(node);
    return node;
}
于 2010-06-07T05:13:02.157 に答える
1

でさらにセッターメソッドを定義しますNote

void number(YAML::Immitter& e) { e>>_number; }

など、次に構文糖衣構文を次のように定義し>>ます

void operator >> ( YAML::Immitter& e, Note& note ) {
  note.number(e);
  note.author(e);
  note.subject(e);
  note.body(e);
}

私はあなたが使用しているYAML名前空間に精通していません(私は知っていますyamlが、C ++でそれを処理したことはありません)が、それはおおよそ通常のストリームで行う方法です(voidリターンタイプを除く;-)、そして私は正確なニーズに簡単に適応できると確信しています。

于 2010-06-07T03:56:59.113 に答える
1

クラスにはすでに setter メソッドがあります。一時変数を使用して値を読み取り、setter メソッドを使用してオブジェクトを構成するだけです。

void operator >> ( const YAML::Emitter& node, Note& note ) {
  unsigned long number;
  unsigned long author;
  // ...
  node[0] >> number;
  node[1] >> author;
  // ... everything properly read, edit the node:
  node.number(number);
  node.author(author);
  // ...
  return;

}

その他のコメント: すべての属性のセッター/ゲッターを持つクラスはほとんどカプセル化されません。フィールドが実際に公開されている場合と同じアクセス レベルをユーザーに与えています (後でチェックを追加できるという唯一の利点がありますが、それでもカプセル化は弱いです)。

YAML ノードを使用するメンバー メソッドを追加することを提案するソリューションでは、クラスのすべてのユーザーに追加の依存関係が追加されます。前方宣言を使用して YAML ヘッダーを強制的に含めることを回避できますが、NoteYAML を簡単に使用しない別のプロジェクトで使用するライブラリをプルすることはできません。

リソースの潜在的な無駄な使用は、おそらく非常に制限されるでしょう。次に、いつものように、まず測定してから、問題がある場合は解決してみてください。

于 2010-06-07T07:23:37.570 に答える
0

さて、ここであなたが考えるかもしれないアイデアがあります。非フレンド、非メンバーの << 関数であなたが抱えている問題は、多くの tmp 宣言が含まれていることです。コンセプトをカプセル化し、その周りに再利用可能なコンポーネントを構築することを検討しましたか? 使用法は次のようになります。


inputter& operator >> (inputter& in, my_type & obj)
{
  input_helper<my_type> helper(obj);

  in >> helper.setter(&my_type::number);
  in >> helper.setter(&my_type::subject);
  // etc
}

の責任は、単純に値を読み取り、それを使用してセッターを呼び出し、必要な一時変数を作成するオブジェクトを返すinput_helperテンプレート関数を提供することです。setter()このようなコードには、テンプレートに精通している必要がありますが、特に難しいことはありません。今は完全にまっすぐに考えることができません-風邪をひいているかもしれません-または、おそらくそれをタイプすることができるでしょう. たぶん、次のようなものです。


template < typename T >
struct input_helper
{
  input_helper(T & t) : obj(t) {}

  template < typename V >
  struct streamer
  {
    streamer(T & t, void (T::*f)(V const&)) : obj(t), fun(f) {}

    template < typename Stream >
    Stream& read_from(Stream & str) const // yeah, that's right...const; you'll be using a temporary.
    {
      V v;
      str >> v;
      obj.(*fun)(v);
      return str;
    }

  private: // you know the drill...
  }

  template < typename V >
  streamer setter(void (T::*fun)(V const&))
  {
    return streamer(obj, fun);
  }
private:
  T & obj;
};
// etc...  operator >> (blah blah) { return setter.read_from(stream); }

そこには確かにあらゆる種類のエラーがありますが、それはあなたにアイデアを与えるはずです. また、一般化するにはさらに多くの作業が必要になります。

于 2010-06-07T05:06:39.910 に答える