Question

J'utilise yaml-cpp pour un projet. Je veux surcharger les opérateurs de << et >> pour certaines classes, mais je vais avoir un problème avec la façon de grappling faire « correctement ». Prenez la classe Note, par exemple. Il est ennuyeux assez:

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

L'opérateur << est facile sauce. Dans le .h:

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

Et dans le .cpp:

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

Pas de sueur. Ensuite, je vais déclarer l'opérateur >>. Dans le .h:

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

Mais dans le .cpp je reçois:

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

Si j'écris des choses comme node[0] >> v._number; alors je aurais besoin de changer le CV-qualifié pour faire tous les champs de Note public (qui contrecarre tout ce que j'appris (par des professeurs, des livres, et de l'expérience))) sur le masquage de données.

Je me sens comme faire node[0] >> temp0; v.number( temp0 ); partout est non seulement fastidieuse, sujette à l'erreur, et laid, mais inutile (ce avec les copies supplémentaires).

Alors je suis sage: J'ai essayé de déplacer ces deux opérateurs dans la classe Note elle-même, et les déclarer comme friends, mais le compilateur (GCC 4.4) n'a pas comme ça:

src / note.h: 44: Erreur: « YAML :: & Remarque :: Emitter opérateur << (YAML :: & Emitter, const Note &) » doivent prendre un seul argument
src / note.h: 45: Erreur: « vide Remarque :: operator >> (const YAML :: node &, Note &) » doit prendre un seul argument

Question: Comment puis-je "correctement" surcharge l'opérateur >> pour une classe

  1. Sans violer le principe de cacher l'information?
  2. Sans la copie excessive?
Était-ce utile?

La solution

La façon typique de le faire sans violer l'encapsulation est de rendre l'opérateur >> une fonction ami. Il devait y avoir un problème de syntaxe avec votre déclaration d'un opérateur ami (pas clair exactement du message d'erreur). Je ne me YAML, mais de votre question qui suit est le jist de celui-ci:

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

Une fonction ami a les mêmes droits d'accès aux membres privés en fonction de membre.

Vous pouvez déclarer setters pour toutes les données membres, mais la méthode de fonction ami est plus propre.

Autres conseils

J'aime utiliser une méthode d'assistance. Puisque la méthode fait partie de la classe, il aura un accès complet à tous les domaines privé:

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

et alors operator>> juste avant l'appel:

const YAML::Node &operator >> ( const YAML::Node& node, Note& note ) {
    note.read(node);
    return node;
}

Vous définissez d'autres méthodes setter dans Note, tels que

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

etc, et vous définissez ensuite la >> syntaxe sucre

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

Je ne suis pas au courant de l'espace de noms YAML que vous utilisez (je sais yaml mais je ne l'ai jamais manié en C ++), mais qui est à peu près comment vous doIT avec des flux normaux (à l'exception des types de retour de void; -), et je suis sûr qu'il peut être facilement adapté à vos besoins

.

Votre classe a déjà des méthodes setter. Il suffit d'utiliser pour lire les temporaires valeurs et d'utiliser les méthodes de réglage pour configurer l'objet:

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;

}

D'autres commentaires: Une classe qui a setters / getters pour tous les attributs est à peine encapsulé. Vous donnez aux utilisateurs le même niveau d'accès que si vos champs étaient réellement publics (avec le seul avantage que vous pouvez ajouter une vérification à une date ultérieure, mais encore, l'encapsulation est faible).

Sur les solutions qui suggèrent d'ajouter une méthode de membre qui prend le nœud YAML, qui ajoutera une dépendance supplémentaire à tous les utilisateurs de votre classe. Alors que vous pouvez utiliser des déclarations avant pour éviter de les forcer à inclure les en-têtes YAML, vous ne serez pas en mesure de tirer une bibliothèque avec votre Note à utiliser dans un autre projet qui n'utilise pas YAML facilement.

Le potentiel gaspillage l'utilisation des ressources va probablement être très limité. Là encore, comme toujours, la première mesure et ensuite essayer de résoudre les problèmes si vous en avez.

Eh bien, voici une idée que vous pourriez envisager. Le problème que vous dites que vous avez avec le non-ami, non-membre << fonction est qu'elle implique beaucoup de déclarations tmp. Avez-vous envisagé encapsulant le concept et la construction d'un composant réutilisable autour d'elle? Utilisez pourrait ressembler à quelque chose comme ceci:


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
}

La responsabilité de input_helper est simplement de fournir la fonction modèle setter() qui retourne un objet qui lit simplement la valeur et appelle le compositeur avec elle, la création de la variable temporaire nécessaire. Code comme ceci nécessiterait une certaine familiarité intime avec des modèles, mais ne serait pas particulièrement difficile. Ne peut pas penser complètement droite en ce moment - peut-être un rhume - ou je serais probablement capable de taper tout simplement. Peut-être que quelque chose sorte de comme ceci:


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

Il y a sûrement toutes sortes d'erreurs dans cela, mais il devrait vous donner l'idée. Il faudrait aussi plus de travail à généraliser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top