14.1 Basic concepts
There is basically nothing to talk about in this section. Pay attention to whether the overloaded operator is chosen as a member or non-member function.
-
Assignment (
=
), subscript ([]
), call (( )
), and member access arrow (->
) operator must be a member. -
Compound assignment operators are usually members, but not required.
-
Operators that change the state of an object or operators that are closely related to a given type, such as increment, decrement, dereference, should usually be members.
-
Operators withsymmetrymayconvert the operands at either end, such asarithmetic, equality, relationaloperators, etc., should be non-members.
When we define an operator as a member function, its left-hand operand must be an object of the class to which the operator belongs.
14.2 Input and output operators
Overloaded input and output operatorsmust be non-member functions.
Because we usually call the output operator in the form of cout << item
. If it is implemented with a member function, it means that the overloaded output operator should be cout
which belongs to the class < A member of code>ostream is obviously unreasonable. We cannot modify ostream
in the standard library.
If it must be implemented as a member function, it can only be a member of item
. At this time, the calling form is item << cout
, which is easy to cause confusion in reference.
It is usually necessary to read and write private data members of the class, which are generally declared as friends.
14.2.1 Overloaded output operators
Usually, the first parameter of an output operator is a reference to a non-constostream
object. Non-const: writing to a stream changes its state; reference: ostream
objects cannot be copied. The second formal parameter is usually a constant reference. Consistent with other output operators, operator<<
returns its ostream
formal parameters.
ostream & amp;operator(ostream & amp;os, const Sales_data & amp;item) // The ostream object cannot be copied and can only return a reference. {<!-- --> // Returning a reference also facilitates continuous output? os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; }
14.2.2 Overloading input operators
Unlike the overloaded output operator, the second parameter of the input operator is usually a non-const reference because the data is to be read into this object.
istream & amp;operator(istream & amp;is, Sales_data & amp;item) {<!-- --> ... // read data if(is) // Check whether the input is successful item.revenue = item.units_sold * price; else item = Sales_data(); // Input failed: object is assigned default constructed state return is; }
14.3 Arithmetic and relational operators
Typically, arithmetic and relational operators are defined as non-member functions to allow conversion of the left-hand or right-hand operand. Generally, there is no need to change the state of the object, and the formal parameters are all constant references. Arithmetic operators usually evaluate two operands and return the result in a temporary object.
If an arithmetic operator is defined, a corresponding compound assignment operator is generally defined. The most efficient way at this time is to define arithmetic operators using compound assignment.
Sales_data operator + (const Sales_data & amp;lhs, const Sales_data & amp;rhs) {<!-- --> Sales_data sum = lhs; lhs + = rhs; return sum; }
14.3.1 Equality operator
As long as one of the equality operator and the inequality operator is actually defined, the other one can be called directly.
bool operator==(const Sales_data & amp;lhs, const Sales_data & amp;rhs) {<!-- --> return lhs.isbn() == rhs.isbn() & amp; & amp; lhs.units_sold == rhs.units_sold & amp; & amp; lhs.revenue == rhs.revenue; } bool operator!=operator(const Sales_data & amp;lhs, const Sales_data & amp;rhs) {<!-- --> return !(rhs == lhs); }
14.3.2 Relational operators
There's nothing to say, more or less than that.
14.4 Assignment Operator
The assignment operator must be defined as a member function of the class. This is usually recommended for compound assignment operators, both of which must return a reference to the left operand.
14.5 Subscript operator
Must be a member function. The subscript operator usually defines two versions, one that returns a normal reference, and one that operates on a constant object and returns a constant reference.
14.6 Increment and decrement operators
It is usually recommended to define it as a member function of the class.
Prefix
The prepended operator should return a reference to the incremented or decremented object.
Posted
The postfix operator should return the original value of the object, returning a value rather than a reference. (Cannot return a reference to a local object)
Adding int
to the parameter can be used to distinguish postfix and prefix.
14.7 Member access operators
It doesn’t feel important, I’ll look back and record it later.
14.8 Function call operator
The function call operator must be a member function. If a class defines a call operator, an object of that class is called a function object.
Function objects can contain data members that are used to customize the operations in the call operator.
Function objects are often used as actual parameters of generic algorithms.
class PrintString {<!-- --> public: PrintString(ostream & amp;o = cout, char c = ''): os(o), sep(c) {<!-- --> } void operator()(const string & amp;s) const {<!-- --> os << s << sep; } private: ostream &os; char sep; } for_each(vec.begin(), vec.end(), PrintString(cerr, '\\ '));
14.8.1 lambda is a function object
In for_each(vec.begin(), vec.end(), PrintString(cerr, '\
, the compiler translates the lambda expression into anunknown Unnamed object of a named classContains an overloaded function call operator in the class generated by the lambda expression.
'))
By default a lambda cannot change the variables it captures. Therefore, by default, the function call operator in the class generated by lambda is a **const
member function**. If the lambda is declared mutable, the call operator is not const
.
Class representing lambda and corresponding capture behavior
-
When a lambda expression captures a variable by reference, the compiler can use the variable directly without copying it to the class generated by the lambda and storing it as a data member.
-
Variables captured by value will be copied to lambda. Corresponding data members must be established in the class generated by lambda and initialized through the constructor.
14.8.2 Function objects defined by the standard library
Use standard library function objects in algorithms:
sort(vec.begin(), vec.end(), less<int>());
Sort vec
in ascending order. The third actual parameter is an unnamed object of type less
. When sort
When comparing elements, the given less
function object will be called.
14.8.3 Callable objects and functions
Callable Objects (Callable Objects
) in C++ include: functions, function pointers, lambda expressions, function objects, bind-generated objects, and function-generated objects.
Like other objects, callable objects also have their own types. For example, lambda has its own unique class type. The types of functions and function pointers are determined by the return value and parameter types, and so on. However, callable objects of different types may share the same calling form, which specifies the return type and parameter type.
Standard library function type: function is a template. When creating a specific function type, you need to give the calling form of the object.
function<int (int, int)> f1 = add; function<int (int, int)> f2 = divide(); // Object of function object class function<int (int, int)> f3 = [](int i, int j) {<!-- --> return i * j; }; // lambda expression
Overloaded functions and functions: You cannot directly store the name of an overloaded function into an object of function type.