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 );到处都做不仅乏味、容易出错和丑陋,而且相当浪费(额外的副本怎么办)。

然后我变得聪明了:我试图将这两个运算符移动到Note类本身中,并将它们声明为friends,但是编译器(GCC 4.4)不喜欢这样:

src/note.h:44: error: 'YAML::Emitter& Note::operator<<(YAML::Emitter&, const Note&)' 必须只取一个参数
src/note.h:45: error: 'void Note: :operator>>(const YAML::Node&, Note&)' 必须只有一个参数

问题:如何“正确”重载>>类的运算符

  1. 在不违反信息隐藏原则的情况下?
  2. 无需过度复制?
4

5 回答 5

3

在不违反封装的情况下执行此操作的典型方法是使 operator>> 成为友元函数。您对友元运算符的声明一定存在语法问题(从错误消息中不清楚究竟是什么)。我不使用 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;
 }

朋友函数对私有成员具有与成员函数相同的访问权限。

或者,您可以为所有成员数据声明 setter,但友元函数方法更简洁。

于 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

您在 中定义更多的 setter 方法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 标头,但您将无法使用您的库拉取一个库以Note用于其他不容易使用 YAML 的项目。

潜在的资源浪费可能非常有限。再说一次,一如既往,首先测量,然后如果有问题,请尝试解决问题。

于 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()返回一个对象,该对象简单地读取值并用它调用 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 回答