Opérateur de surcharge C ++ Primer et conversion-entrée / sortie, arithmétique / relation

  L'interface d'opération d'E / S fournie par la classe qui prend en charge les opérations d'E / S doit généralement être la même que l'interface définie par la bibliothèque standard iostream pour les types intégrés. Par conséquent, de nombreuses classes nécessitent une surcharge des opérateurs d'entrée et de sortie.
  
  1. La surcharge de l'opérateur de sortie <<
  
  Pour être cohérent avec la bibliothèque standard IO, l'opérateur doit accepter ostream & comme premier paramètre formel, la référence à l'objet de type classe const comme second paramètre formel et renvoyer la référence du paramètre formel ostream !
  
  ostream & operator << (ostream & os, const ClassType & object)
  
  {
  
  os << // ....
  
  return os;
  
  }
  
  1. Opérateur de sortie
  
  Sales_item ostream & operator << (ostream & out, const Sales_item & object)
  
  {
  
  out << object. isbn << '\ t' << object.units_sold << '\ t'
  
  << object.revenue << '\ t' << object.avg_price ();
  
  return out;
  
  }
  
  2. Les opérateurs de sortie doivent généralement faire le
  
  moins de mise en forme possible. De manière générale, les opérateurs de sortie doivent sortir le contenu de l'objet et la mise en forme minimale doit être effectuée. Ils ne doivent pas sortir de retours à la ligne! Minimisez le plus possible le formatage des opérateurs, permettant aux utilisateurs de contrôler eux-mêmes les détails de sortie.
  
  Élément Sales_item ("C ++ Primer");
  
  cout << item << endl; // L'utilisateur contrôle les sauts de ligne de sortie
  
  3. L'opérateur IO doit être une fonction non membre
  
  Nous ne pouvons pas définir l'opérateur comme un membre de la classe, sinon l'opérateur gauche ne peut être qu'un objet de ce type:
  
  ostream & Sales_item :: operator << (ostream & out)
  
  {
  
  out << isbn << '\ t' << units_sold << '\ t'
  
  << revenue << '\ t' << avg_price ();
  
  return out;
  
  }
  
  // Test de l'
  
  élément Sales_item ("C ++ Primer");
  
  // this L'utilisation est exactement l'opposé de l'
  
  élément d' utilisation normale << cout << endl;
  
  // OU
  
  item.operator << (cout);
  
  // Erreur
  
  cout << item << endl;
  
  Si vous souhaitez prendre en charge une utilisation normale, l'opérande de gauche doit Il est de type ostream. Cela signifie que si l'opérateur est membre de la classe, il doit être membre de la classe ostream, cependant, la classe ostream fait partie de la bibliothèque standard, et nous (et quiconque souhaite définir un opérateur IO) ne pouvons pas être un standard Les classes de la bibliothèque ajoutent des membres.
  
  Étant donné que l'opérateur IO lit et écrit généralement des membres de données non publics, la classe définit généralement l'opérateur IO en tant qu'ami.
  
  // P437 Exercice 14.
  

  

  
  ami ostream & operator << (ostream & os, const CheckoutRecord & object);
  
  public:
  
  typedef unsigned Date;
  
  // ...
  
  privé:
  
  double book_id;
  
  titre de chaîne;
  
  Date date_borrowed;
  
  Date date_due;
  
  paire <chaîne, chaîne> emprunteur;
  
  vecteur <paire <chaîne, chaîne> *> liste d'attente;
  
  };
  
  ostream & operator << (ostream & os, const CheckoutRecord & obj)
  
  {
  
  os << obj.book_id << ":" << obj.title << '\ t' << obj.date_borrowed
  
  << '\ t' << obj. date_due << '\ t' << obj.borrower.first << ''
  
  << obj.borrower.second << endl;
  
  os << "
  

  
  ! = Obj.wait_list.end ITER (); ITER ++)
  
  {
  
  OS << (* ITER) -> << Première \ 'T' << (* ITER) -> DEUXIÈME << endl;
  
  }
  
  }
  
  II entrée La surcharge de l'opérateur >>
  
  est similaire à l'opérateur de sortie. Le premier paramètre de l'opérateur d'entrée est une référence au flux qu'il veut lire, et le retour est également une référence au même flux. Son deuxième paramètre formel est une référence non const à l'objet à lire. Le paramètre formel doit être non const car le but de l'opérateur d'entrée est de lire des données dans cet objet.
  
  L'opérateur d'entrée doit gérer les erreurs et la possibilité de fin de fichier!
  
  1. Opérateur d'entrée
  
  istream & operator >> (istream & in, Sales_item & s)
  
  {
  
  double price;
  
  in >> s.isbn >> s.units_sold >> price;
  
  if (in)
  
  {
  
  s.revenue = price * s. units_sold;
  
  }
  
  else
  
  {
  
  // Si la lecture échoue, réinitialisez l'objet à l'état par défaut
  
  s = Sales_item ();
  
  }
  
  return in;
  

  

  
  Les erreurs possibles incluent:
  
  1) Toute opération de lecture peut échouer car la valeur fournie est incorrecte. Par exemple, après avoir lu dans isbn, l'opérateur de saisie s'attend à ce que les deux éléments suivants soient des données numériques. Si vous entrez des données non numériques, cette lecture et l'utilisation ultérieure du flux échouera.
  
  2) Toute lecture peut rencontrer la fin du fichier ou d'autres erreurs dans le flux d'entrée.
  
  Mais nous n'avons pas besoin de vérifier chaque fois que nous lisons, il suffit de le vérifier une fois avant d'utiliser les données lues.
  
  if (in)
  
  {
  
  s.revenue = price * s.units_sold;
  
  }
  
  else
  
  {
  
  s = Sales_item ();
  
  }
  
  S'il y a une erreur, nous ne nous soucions pas de l'entrée qui a échoué, à la place, nous réinitialisons l'objet entier!
  
  3. Gérer les erreurs d'entrée
  
  Si l'opérateur d'entrée détecte que l'entrée a échoué, il est recommandé de s'assurer que l'objet est disponible et cohérent! Ceci est particulièrement important si l'objet a écrit des informations avant l'erreur!
  
  Par exemple, dans l'opérateur d'entrée de Sales_item, un nouvel isbn peut être lu avec succès, puis une erreur de flux est rencontrée. Une erreur après avoir lu dans l'isbn signifie que les membres units_sold et revenue de l'ancien objet n'ont pas changé, et par conséquent, un autre isbn sera associé à ces données (tragique ...). Par conséquent, le renvoi du paramètre formel à un objet Sales_item vide peut éviter de lui donner un état non valide!
  
  [Meilleure pratique] Lors de la
  
  conception des opérateurs d'entrée, il est important de déterminer les mesures de récupération d'erreur si possible!
  
  4. Soulignez les erreurs
  
  En plus de gérer les erreurs qui peuvent survenir, les opérateurs d'entrée peuvent également avoir besoin de définir l'état conditionnel des paramètres d'entrée.
  
  Certains opérateurs d'entrée nécessitent des vérifications supplémentaires. Par exemple, notre opérateur d'entrée peut vérifier si le format isbn lu est approprié. Nous avons peut-être lu les données avec succès, mais les données ne peuvent pas être correctement interprétées comme ISBN. Dans ce cas, bien que l'IO réel soit techniquement réussi, l'opérateur de saisie peut encore avoir besoin de définir l'état conditionnel pour indiquer l'échec. Habituellement, l'opérateur d'entrée n'a besoin que de définir le failbit. La définition de eofbit signifie que le fichier est épuisé, et la définition de badbit peut indiquer que le flux est endommagé. Il est préférable de laisser ces erreurs à la bibliothèque IO standard pour les signaler.
  
  // P439 Exercice 14.11
  
  classe CheckoutRecord
  
  {
  
  ami istream & operator >> (istream & in, CheckoutRecord & object);
  
  public:
  
  typedef unsigned Date;
  
  // ...
  
  private:
  
  double book_id;
  
  string title;
  
  Date date_borrowed;
  
  Date date_due;
  
  pair <string, chaîne> emprunteur;
  
  vecteur <paire <chaîne, chaîne> *> liste d'attente;
  
  };
  
  istream & operator >> (istream & in, CheckoutRecord &
  

  
  dans >> obj.book_id >> obj.title >> obj.date_borrowed >> obj.date_due;
  
  dans >> obj.borrower.first >> obj.borrower.second;
  
  if (! in)
  
  {
  
  obj = CheckoutRecord ();
  
  return in;
  
  }
  
  obj.wait_list.clear ();
  
  while (in)
  
  {
  
  pair <string, string> * p = new pair <string, string>;
  
  in >> p-> first >> p-> second;
  
  if ( in)
  
  {
  
  obj.wait_list.push_back (p);
  
  delete p;
  
  }
  
  }
  
  return in;
  
  }
  
  3. Opérateurs arithmétiques De
  
  manière générale, les opérateurs arithmétiques et relationnels sont définis comme des fonctions non membres:
  
  opérateur Sales_item + (const Sales_item & lhs, const Sales_item & rhs)
  
  {
  
  Sales_item ret (lhs);
  
  // Utilisez l'opérateur de copie composé Sales_item pour ajouter la valeur de rhs
  
  ret + = rhs;
  
  return ret;
  
  } L'
  
  opérateur d'addition ne modifie pas l'état de l'opérande, qui fait référence à l'objet const.
  
  [Meilleure pratique]
  
  Afin d'être cohérent avec les opérateurs intégrés, l'addition renvoie une valeur r, pas une référence!
  
  L'opérateur arithmétique et la classe des opérateurs d'affectation composés sont définis. En règle générale, l'affectation composée doit être utilisée pour implémenter les opérateurs arithmétiques.
  
  // P440 Problème 14,12
  
  l'opérateur Sales_item + (const le Sales_item & LHS, RHS et const le Sales_item)
  
  {
  
  le Sales_item tmp;
  
  tmp.units_sold + = lhs.units_sold rhs.units_sold;
  
  tmp.revenue + = lhs.revenue rhs.revenue;
  
  retour tmp;
  
  }
  
  le Sales_item le Sales_item & :: operator + = (const Sales_item & rhs)
  
  {
  
  * this = * this + rhs;
  
  return * this;
  
  }
  
  Quatrièmement, l'opérateur relationnel
  
  1, l'opérateur d'égalité
  
  Si tous les membres correspondants sont égaux, les deux objets sont considérés comme égaux.
  

  
  opérateur booléen en   ligne == (const Sales_item & lhs, const Sales_item & rhs)
  
  {
  
  return lhs.revenue == rhs.revenue && lhs.units_sold == rhs.units_sold &&
  
  lhs.same_isbn (rhs);
  
  }
  
  inline
  
  bool operator! = (const Sales_ite & lhs, const Sales_item & rhs)
  
  {
  
  return! (lhs == rhs);
  
  }
  
  1) Si la classe définit l'opérateur ==, la signification de l'opérateur est que deux objets contiennent les mêmes données.
  
  2) Si la classe a une opération qui peut déterminer si deux objets de ce type sont égaux, la fonction est généralement définie comme opérateur == au lieu de créer une fonction nommée. Les utilisateurs seront habitués à utiliser == pour comparer des objets, ce qui est plus facile que de se souvenir de nouveaux noms.
  
  3) Si la classe définit operator ==, elle doit également définir operator! =. Les utilisateurs s'attendront à ce que si un opérateur est disponible, un autre existera également.
  
  4) L'égalité et le non-opérateur doivent généralement être définis l'un par rapport à l'autre, laisser un opérateur terminer le travail réel de l'objet de comparaison, et l'autre opérateur appelle simplement le premier.
  
  Les classes avec l'opérateur == défini sont plus faciles à utiliser avec la bibliothèque standard. Certains algorithmes, comme find, utilisent par défaut l'opérateur == Si la classe définit ==, ces algorithmes peuvent être utilisés pour ce type sans aucun traitement spécial!
  
  2. Opérateurs relationnels
  
  Les classes qui définissent les opérateurs d'égalité ont généralement également des opérateurs relationnels. En particulier, étant donné que le conteneur associatif et certains algorithmes utilisent l'opérateur inférieur à (<), il peut être très utile de définir l'opérateur <.
  
  Si la définition logique de <n'est pas cohérente avec la définition logique de ==, dans ce cas, il vaut mieux ne pas définir <.
  
  [Note]
  
  Les conteneurs associés et certains algorithmes utilisent l'opérateur <par défaut (je pense que la traduction du traducteur est erronée ici, l'original: ... utilise l'opérateur <par défaut ...), le traducteur se traduit par: utilise l'opérateur <par défaut, Mais je pense que la valeur par défaut est plus appropriée!). En général, les opérateurs relationnels, tels que les opérateurs d'égalité, doivent être définis comme des fonctions non membres (opérateurs "symétriques").

Je suppose que tu aimes

Origine www.cnblogs.com/zhenhua1618/p/12729591.html
conseillé
Classement