Chapter.14 Overloaded Operations and Conversions

1.Basic Concepts

What is the overloaded operator??

Overloaded operators are functions with special names: the keyword operator followed by the symbol for the operator being defined. Like any other function, an overloaded operator has a return type, a parameter list, and a body

note:

When an overloaded operator is a member function, this is bound to the left-hand operand. Member operator functions have one less (explicit) parameter than the number of operands.

note:

An operator function must either be a member of a class or have at least one parameter of class type

// error: cannot redefine the built-in operator for ints
int operator+(int, int);



Calling an Overloaded Operator Function Directly

(1)non-member function

// equivalent calls to a nonmember operator function
data1 + data2;           // normal expression
operator+(data1, data2); // equivalent function call
(2)member function
data1 += data2; // expression-based ''call''
data1.operator+=(data2); // equivalent call to a member operator

Each of these statements calls the member function operator+=, binding this to the address of data1 and passing data2 as an argument


Best Practices
Ordinarily, the comma, address-of, logical AND , and logical OR operators should not be overloaded

,
&
&&
||

Use Definitions That Are Consistent with the Built-in Meaning

If the class does IO, define the shift operators to be consistent with how IO is done for the built-in types.
(1)If the class has an operation to test for equality, define operator==. If the class has operator==, it should usually have operator!= as well.
(2)If the class has a single, natural ordering operation, define operator<. If the class has operator<, it should probably have all of the relational operators.
(3)The return type of an overloaded operator usually should be compatible with the return from the built-in version of the operator: 

        The logical and relational operators should return bool,

        The arithmetic operators should return a value of the class type, and assignment and compound assignment should return a reference to the left-hand operand


Choosing Member or Nonmember Implementation

(1)、The assignment (=), subscript ([]), call (()), and member access arrow (->) operators must be defined as members.
(2)、The compound-assignment operators ordinarily ought to be members. However, unlike assignment, they are not required to be members.
(3)、Operators that change the state of their object or that are closely tied to their given type—such as increment, decrement, and dereference usually should be members.
(4)、Symmetric operators(對稱運算符)—those that might convert either operand, such as the arithmetic, equality, relational, and bitwise operators—usually should be defined as ordinary nonmember functions


Exercises:

In what ways does an overloaded operator differ from a built-in operator? In what ways are overloaded operators the same as the built-in operators?

Differ 1. We can call an overloaded operator function directly. 2. An overloaded operator function must either be a member of a class or have at least one parameter of class type. 3. A few operators guarantee the order in which operands are evaluated. These overloaded versions of these operators do not preserve order of evaluation and/or short-circuit evaluation, it is usually a bad idea to overload them.

In particular, the operand-evaluation guarantees of the logical AND, logical OR, and comma operators are not preserved, Moreover, overloaded versions of && or || operators do not preserve short-circuit evaluation properties of the built-in operators. Both operands are always evaluated.

Same

  • An overloaded operator has the same precedence and associativity as the corresponding built-in operator


Explain how to decide whether the following should be class members:

  • (a) %    symmetric operator. Hence, non-member
  • (b) %=  changing state of objects. Hence, member
  • (c) ++   changing state of objects. Hence, member
  • (d) ->    = () [] -> must be member
  • (e) <<   non-member
  • (f) &&   symetric , non-member
  • (g) ==  symetric , non-member
  • (h) ()    = () [] -> must be member



2、Input and Output Operators

(1)Overloading the Output Operator <<

      Ordinarily, the first parameter of an output operator is a reference to a nonconst ostream object. The ostream is nonconst because writing to the stream changes its state. The parameter is a reference because we cannot copy an ostream object

      The second parameter ordinarily should be a reference to const of the class type we want to print. The parameter is a reference to avoid copying the argument. It can be const because (ordinarily) printing an object does not change that object.

note:

Generally, output operators should print the contents of the object, with minimal formatting. They should not print a newline.

IO Operators Must Be Nonmember Functions


(2)Overloading the Input Operator >>

Ordinarily the first parameter of an input operator is a reference to the stream from which it is to read, and the second parameter is a reference to the (nonconst) object into which to read. The operator usually returns a reference to its given stream. The second parameter must be nonconst because the purpose of an input operator is to read data into this object


note:

Input operators must deal with the possibility that the input might fail; output operators generally don’t bother.Input operators should decide what, if anything, to do about error recovery.


Sales_data 

#ifndef CP5_CH14_EX14_02_H
#define CP5_CH14_EX14_02_H

#include <string>
#include <iostream>

class Sales_data {
    friend std::istream& operator>>(std::istream&, Sales_data&);       // input
    friend std::ostream& operator<<(std::ostream&, const Sales_data&); // output
    friend Sales_data operator+(const Sales_data&, const Sales_data&); // addition

public:
    Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }
    Sales_data() : Sales_data("", 0, 0.0f){ }
    Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ }
    Sales_data(std::istream &is);

    Sales_data& operator+=(const Sales_data&); // compound-assignment
    std::string isbn() const { return bookNo; }

private:
    inline double avg_price() const;

    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

std::istream& operator>>(std::istream&, Sales_data&);
std::ostream& operator<<(std::ostream&, const Sales_data&);
Sales_data operator+(const Sales_data&, const Sales_data&);

inline double Sales_data::avg_price() const
{
    return units_sold ? revenue/units_sold : 0;
}

#endif // CP5_CH14_EX14_02_H


#include "ex14_02.h"

Sales_data::Sales_data(std::istream &is) : Sales_data()
{
    is >> *this;
}

Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

std::istream& operator>>(std::istream &is, Sales_data &item)
{
    double price = 0.0;
    is >> item.bookNo >> item.units_sold >> price;
    if (is)
        item.revenue = price * item.units_sold;
    else
        item = Sales_data();
    return is;
}

std::ostream& operator<<(std::ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
    return os;
}

Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
    Sales_data sum = lhs;
    sum += rhs;
    return sum;
}

3. Arithmetic and Relational Operators

     Ordinarily, we define the arithmetic and relational operators as nonmember functions in order to allow conversions for either the left- or right-hand operand (§ 14.1, p. 555). These operators shouldn’t need to change the state of either operand, so the parameters are ordinarily references to const

(1).Equality Operators

Tip

Classes that define both an arithmetic operator and the related compound assignment ordinarily ought to implement the arithmetic operator by using the compound assignment.

// assumes that both objects refer to the same book
Sales_data
operator+(const Sales_data &lhs, const Sales_data &rhs)
{
     Sales_data sum = lhs; // copy data members from lhs into sum
     sum += rhs; // add rhs into sum
     return sum;
}

Classes for which there is a logical meaning for equality normally should define operator==. Classes that define == make it easier for users to use the class with the library algorithms

bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn() &&
    lhs.units_sold == rhs.units_sold &&
    lhs.revenue == rhs.revenue;
}
bool operator!=(const Sales_data &lhs, const Sales_data &rhs)
{
    return !(lhs == rhs);
}

(2).Relational Operators

Best Practices
If a single logical definition for < exists, classes usually should define the < operator. However, if the class also has ==, define < only if the definitions of < and == yield consistent results

4、Assignment Operators

the copy- and move-assignment operators assignment .  operator that takes a braced list of elements

Assignment operators can be overloaded. Assignment operators, regardless of parameter type, must be defined as member functions



發佈了146 篇原創文章 · 獲贊 240 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章