代码之家  ›  专栏  ›  技术社区  ›  Chris Tonkinson

重载流插入而不违反信息隐藏?

  •  1
  • Chris Tonkinson  · 技术社区  · 15 年前

    我在用 yaml-cpp 为了一个项目。我想让飞机超载 << >> 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; 然后我需要更改简历限定符,使所有的 注意 领域 public

    node[0] >> temp0; v.number( temp0 ); 到处都是,不仅乏味,容易出错,丑陋,但相当浪费(与额外的副本)。

    注意 类本身,并将它们声明为 friend s、 但是编译器(GCC 4.4)不喜欢这样:


    src/note.h:45:错误:void note::operator>&燃气轮机(const YAML::节点&,注释(&)必须只接受一个参数

    问题: 我该如何“恰当地”过载 >>

    1. 不违反信息隐藏原则?
    2. 没有过度复制?
    5 回复  |  直到 15 年前
        1
  •  3
  •   academicRobot    15 年前

    在不违反封装的情况下执行此操作的典型方法是使运算符>&燃气轮机;好友功能。您的友元运算符声明一定有语法问题(不清楚错误消息中的确切内容)。我不使用YAML,但从您的问题来看,以下是它的jist:

    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;
     }
    

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

        2
  •  3
  •   R Samuel Klatchko    15 年前

    我喜欢使用助手方法。由于该方法是类的一部分,因此它可以完全访问所有私有字段:

    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;
    }
    
        3
  •  1
  •   Alex Martelli    15 年前

    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 返回类型;-),我相信它可以很容易地适应你的确切需要。

        4
  •  1
  •   David Rodríguez - dribeas    15 年前

    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的不同项目中使用。

    潜力 浪费 资源的使用可能会非常有限。然后,像往常一样,首先测量,然后尝试解决问题,如果你有他们。

        5
  •  0
  •   Edward Strange    15 年前

    
    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); }
    

    这里面肯定有各种各样的错误,但它应该给你一个想法。也需要更多的工作来概括。