C ++ Primer overload operator and conversion-input / output, arithmetic / relation

  The I / O operation interface provided by the class that supports I / O operations should generally be the same as the interface defined by the standard library iostream for the built-in types. Therefore, many classes require overloading of input and output operators.
  
  1. The overload of the output operator <<
  
  In order to be consistent with the IO standard library, the operator should accept ostream & as the first formal parameter, the reference to the class type const object as the second formal parameter, and return the reference of the ostream formal parameter !
  
  ostream & operator << (ostream & os, const ClassType & object)
  
  {
  
  os << // ....
  
  return os;
  
  }
  
  1. Sales_item output operator
  
  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. The output operators should generally do as
  
  little formatting as possible. Generally speaking, the output operators should output the contents of the object, and the minimum formatting should be done. They should not output newlines! Minimize the formatting of operators as much as possible, allowing users to control the output details themselves.
  
  Sales_item item ("C ++ Primer");
  
  cout << item << endl; // The user controls the output line breaks
  
  3. The IO operator must be a non-member function
  
  We cannot define the operator as a member of the class, otherwise, the left operator can only be an object of this type:
  
  ostream & Sales_item :: operator << (ostream & out)
  
  {
  
  out << isbn << '\ t' << units_sold << '\ t'
  
  << revenue << '\ t' << avg_price ();
  
  return out;
  
  }
  
  // Test
  
  Sales_item item ("C ++ Primer");
  
  // this Usage is exactly the opposite of normal usage
  
  item << cout << endl;
  
  // OR
  
  item.operator << (cout);
  
  // Error
  
  cout << item << endl;
  
  If you want to support normal usage, the left operand must It is of ostream type. This means that if the operator is a member of the class, it must be a member of the ostream class, however, the ostream class is part of the standard library, and we (and anyone who wants to define an IO operator) cannot be a standard Classes in the library add members.
  
  Because the IO operator usually reads and writes non-public data members, the class usually sets the IO operator as a friend.
  
  // P437 Exercise 14.
  

  

  
  friend ostream &operator<<(ostream &os,const CheckoutRecord &object);
  
  public:
  
  typedef unsigned Date;
  
  //...
  
  private:
  
  double book_id;
  
  string title;
  
  Date date_borrowed;
  
  Date date_due;
  
  pair<string,string> borrower;
  
  vector< pair<string,string> * > wait_list;
  
  };
  
  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 << "Wait_list:" << endl;
  
  for (vector< pair<string,string> * >::const_iterator iter = obj.wait_list.begin();
  
  ! = obj.wait_list.end ITER (); ITER ++)
  
  {
  
  OS << (* ITER) -> << First '\ T' << (* ITER) -> SECOND << endl;
  
  }
  
  }
  
  II input The overload of operator >>
  
  is similar to the output operator. The first parameter of the input operator is a reference to the stream it wants to read, and the return is also a reference to the same stream. Its second formal parameter is a non-const reference to the object to be read in. The formal parameter must be non-const because the purpose of the input operator is to read data into this object.
  
  The input operator must deal with errors and the possibility of end of file!
  
  1. Input operator
  
  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
  
  {
  
  // If the reading fails, reset the object to the default state
  
  s = Sales_item ();
  
  }
  
  return in;
  

  

  
  Possible errors include:
  
  1) Any read operation may fail because the value provided is incorrect. For example, after reading in isbn, the input operator will expect the next two items to be numeric data. If you enter non-numeric data, this reading and subsequent use of the stream will fail.
  
  2) Any reading may encounter the end of file or other errors in the input stream.
  
  But we don't need to check every time we read in, just check it once before using the read data.
  
  if (in)
  
  {
  
  s.revenue = price * s.units_sold;
  
  }
  
  else
  
  {
  
  s = Sales_item ();
  
  }
  
  If there is an error, we do n’t care which input failed, instead, we reset the entire object!
  
  3. Handle input errors
  
  If the input operator detects that the input has failed, it is a good practice to ensure that the object is available and consistent! This is especially important if the object has written some information before the error!
  
  For example, in the input operator of Sales_item, a new isbn may be successfully read, and then a stream error is encountered. An error after reading in the isbn means that the units_sold and revenue members of the old object have not changed, and as a result will associate another isbn with that data (tragic ...) Therefore, returning the formal parameter to an empty Sales_item object can avoid giving him an invalid state!
  
  [Best Practice] When
  
  designing input operators, it is important to determine error recovery measures if possible!
  
  4. Point out errors
  
  In addition to handling any errors that may occur, input operators may also need to set the conditional status of input parameters.
  
  Some input operators do require additional checks. For example, our input operator can check whether the read isbn format is appropriate. Maybe we have successfully read the data, but the data cannot be properly interpreted as ISBN. In this case, although the actual IO is technically successful, the input operator may still need to set the conditional status to indicate failure. Usually the input operator only needs to set failbit. Setting eofbit means that the file is exhausted, and setting badbit can indicate that the stream is damaged. These errors are best left to the IO standard library to point out.
  
  // P439 Exercise 14.11
  
  class CheckoutRecord
  
  {
  
  friend istream & operator >> (istream & in, CheckoutRecord & object);
  
  public:
  
  typedef unsigned Date;
  
  // ...
  
  private:
  
  double book_id;
  
  string title;
  
  Date date_borrowed;
  
  Date date_due;
  
  pair <string, string> borrower;
  
  vector <pair <string, string> *> wait_list;
  
  };
  
  istream & operator >> (istream & in, CheckoutRecord &
  

  
  in >> obj.book_id >> obj.title >> obj.date_borrowed >> obj.date_due;
  
  in >> 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;
  
  }
  
  three, arithmetic operators
  
  Generally, the arithmetic and relational operators defined as non-member function:
  
  the Sales_item operator + (const the Sales_item LHS &, const Sales_item & rhs)
  
  {
  
  Sales_item ret (lhs);
  
  // Use the Sales_item compound copy operator to add the value of rhs
  
  ret + = rhs;
  
  return ret;
  
  } The
  
  addition operator does not change the state of the operand, which is a reference to the const object.
  
  [Best practice]
  
  In order to be consistent with the built-in operators, addition returns an rvalue, not a reference!
  
  Both the arithmetic operator and the class of compound assignment operators are defined. Generally, compound assignment should be used to implement arithmetic operators.
  
  // P440 Problem 14.12
  
  the Sales_item operator + (const the Sales_item & LHS, RHS & const the Sales_item)
  
  {
  
  the Sales_item tmp;
  
  tmp.units_sold + = lhs.units_sold rhs.units_sold;
  
  tmp.revenue + = lhs.revenue rhs.revenue;
  
  return tmp;
  
  }
  
  the Sales_item the Sales_item & :: operator + = (const Sales_item & rhs)
  
  {
  
  * this = * this + rhs;
  
  return * this;
  
  }
  
  Fourth, the relational operator
  
  1, the equality operator
  
  If all corresponding members are equal, the two objects are considered equal.
  
  inline
  
  bool operator == (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) If the class defines the == operator, the meaning of the operator is that two objects contain the same data.
  
  2) If the class has an operation that can determine whether two objects of that type are equal, the function is usually defined as operator == instead of creating a named function. Users will be accustomed to using == to compare objects, and doing so is easier than remembering new names.
  
  3) If the class defines operator ==, it should also define operator! =. Users will expect that if one operator is available, another will also exist.
  
  4) The equality and non-operator should generally be defined in relation to each other, let one operator complete the actual work of the comparison object, and the other operator just calls the former.
  
  Classes with operator == defined are easier to use with the standard library. Some algorithms, such as find, use the == operator by default. If the class defines ==, these algorithms can be used for that type without any special processing!
  
  2. Relational operators
  
  Classes that define equality operators generally also have relational operators. In particular, because the associative container and some algorithms use the less-than operator (<), it may be quite useful to define operator <.
  
  If the logical definition of <is inconsistent with the logical definition of ==, so in this case, it is better not to define <.
  
  [Note]
  
  Associated containers and certain algorithms use the <operator by default (I think the translator ’s translation is wrong here, the original: ... usethe <operator bydefult ..., the translator translates to: use the default <operator, But I think the default is more appropriate!). In general, relational operators, such as equality operators, should be defined as non-member functions ("symmetric" operators).

Guess you like

Origin www.cnblogs.com/zhenhua1618/p/12729591.html