except for overloaded function call operators operator (), other overloaded operators cannot contain default arguments. When an overloaded operator is a member function, this is bound to the left operand. The (Explicit) parameter of a member operator function is one less than the number of operands. When an operator acts on an operand of a built-in type, we cannot change the meaning of the operation. :: .* . ?: These four operators cannot be overloaded. We can only reload existing operators and not invent new ones. The precedence and binding laws are the same. Data1 + data2;operator+ (DATA1,DATA2); The above two are equivalent calls. Because overloaded operators are actually function calls, the order rules for the arithmetic objects of logic and, logic or, and commas cannot be preserved, and therefore should not be overloaded. There's another reason. We do not overload the comma and accessor operators: the C + + language already defines the special meaning of the two operators for class-type objects, so they are generally not overloaded, otherwise their behavior will be different from the normal, causing the class user to be unable to adapt.
Assignment operations, the values of the left and right operands are equal, and the operator should return a reference to its left operand.
member functions are also non-member functions: Assignment (=), subscript ([]), Call (()), and member access Arrows (-) operators must be member functions. Composite operators should generally be members, but not required, and only a little bit different from assignment operations. An operator that alters the state of an object, or an operator that is closely related to a given type, such as an increment, decrement, and reference operator, should normally be a member. A symmetric operator might convert an operand at either end, such as arithmetic, want-to-equal, relational, and bitwise operators, so they should normally be ordinary non-member functions. If we want to provide a mixed expression containing a class object, the operator must be defined as a non-member function. such as int and double Add. When we define an operator as a member function, its left operand must be an object of the class to which the operator belongs.
The first parameter of the output operator is a reference to a very ostream object, because the write content changes its state, which is a reference because we cannot copy a Ostream object directly. The second parameter is typically a reference to a constant that makes the class type that we want to print. The reference is because we want to avoid copying the arguments. Constants do not change the content. In order to be consistent with others general return ostream parameter Ostream &operator<< (ostream &os, const sales_data &item) {os<< ITEM.ISBN () << "<<item.units_sold<<" "<<item.revenue <<" "<<item.avg_price (); return OS;} The output operator should minimize formatting operations and allow the user to control the output details. Line breaks should not be printed an input-output operator that is compatible with the Iostream standard library must be a normal sub-member function
The first parameter of the input operator is the flow amount reference that the operator will read, and the second parameter is a reference to the (very) object that will be read into, and typically returns a reference to a given stream. IStream &operator>> (IStream &is, Sales_data &item) {double price; is >> item.bookno >>item.units_sold >>price; if (IS) item.revenue = Item.units_sold * PRICE; else item = Sales_data (); return is;} The input operator must handle situations where the input may fail, and the output operator is not required. The following error may occur when you perform an input operation: the read operation may fail when the stream contains data of the wrong type. When the read operation reaches the end of the file or encounters other errors in the input stream, it also fails. When an error occurs with the read operation, the input operator should be responsible for recovering from the error. Although we can use error markers such as Failbit,eofbit,badbit, it is best to have the standard library mark errors.
Arithmetic and relational operators are defined as non-member functions to allow the conversion of left and right operands. Because these operators generally do not need to change the state of operands, formal parameters are constant references. If an arithmetic operator is defined, a corresponding compound operator is also generally defined. Conforming to an allowable character should generally be a member, but not necessary, only a little different from the assignment operation. If the class also defines arithmetic operations that conform to the associated compound assignment operator, you should typically use the compound assignment operator to implement the arithmetic operator. Sales_data operator+ (const sales_data &LHS, const sales_data &rhs) {sales_data sum = LHS; sum + = RHS; return sum;}
BOOL operator = = (CONST sales_data &LHS, const sales_data &RHS) {return LHS.ISBN () ==RHS.ISBN () && lhs.u Nits_sold==rhs.units_sold && lhs.revenue = = rhs.revenue;} BOOL operator! = (cosnt sales_data &lhs, const sales_data &RHS) {return! LHS = = RHS);} If a class logically has an equal meaning, then operator = = should be defined.
Especially because associative containers and some algorithms use less-than-operator, the operator< you want to define is more useful. If there is only one logical, reliable < definition, you should consider defining the < operator for this class. If the class also contains = =, this is defined as the < operator only if the definition of < = = is the same as the national salvation produced by = =.
In addition to copying assignments and moving assignments, classes can also define additional assignment operators to use other types as the right operand. Vector, for example, defines the third assignment operator, the element list as an argument. Vector<sring> v;v = {"A", "an", "the"}; Add this operator for Strvec. Class Strvec{public:strvec &operator = (std::initializer_list<std::string>);}; strvec& strvec::operator = (std::initializer_list<std::string>)
{Auto data = Alloc_n_copy (Il.begin (), Il.end ()); Free (); elements = Data.first; First_free = Cap =data.second; return *this;} The assignment operator must be a member function.
The compound assignment operator does not have to be a member of a class, but we prefer to define all assignment operations, including compound assignments, within the class. Consistent with the standard library, returns a reference to its left operand sales_data& sales_data::operator + = (const sales_data &RHS) {units_sold + = Rhs.units_sold; Revenue + = Rhs.revenue; return *this;}
The subscript operator must be a member function. For compatibility with the original definition. The subscript returns a reference to the access element. The advantage of this is that the subscript can appear at either end of the assignment operator. Further, we'd better define the constant version and the very version of the subscript operation, and when a constant object is applied, the subscript operator returns a constant reference to ensure that we do not assign a value to the returned object.
Class strvec{public:std::string& operator[] (std::size_t N) {return elements[n];} Const std::string& operator[] (std::size_t N) const {return elements[n];} Private:std::string *elements;};
There are usually increment and decrement operators in the iterator class, which do not have to be member functions, but because they change exactly the state of the object being manipulated, it is recommended that you use member functions. To be consistent with the built-in version, the predecessor returns a reference to the incremented object. After-value rice is the object's original value, and the returned form is a value instead of a reference. Predecessor version class strblobptr{public:strblobptr& operator++ (); strblobptr& operator--();}; strblobptr& strblobptr::operator++ () {///If the Curr has already pointed to the back position of the container, it cannot increment it check (Curr, "increment past end of Strblobptr"); ++curr; return *this;
}strblobptr& strblobptr::operator--() {///if Curr is 0, then continue to decrement it will produce an invalid subscript--curr; If Curr is already 0, then the value we pass in a check will be a very large positive integer check (Curr, "decrement past begin of Strblobptr") that represents an invalid subscript; return *this;}
To differentiate between pre-and post-build versions, the backend version accepts a parameter of an additional int type. Just to differentiate between the pre-and post-function class strblobptr{public:strblobptr operator++ (int); Strblobptr operator--(int);}; Strblobptr strblobptr::operator++ () {///here Invalid check validity, only need to check when the predecessor increment operation is called. STRBLOBPTR ret = *this; ++*this; return ret;} Strblobptr strblobptr::operator--(int) {STRBLOBPTR ret = *this; --*this; return ret;} Our post-build is done with the predecessor version. Because we don't use int parameters, we don't need to name them. Display call: Strblobptr P (AL);p. operator++ (0); Post-build p.operator++ ();//pre-built version
The dereference operator (*) and the Arrow operator (-) class strblobptr{public:std::string& operator* () const {Auto p are often used in iterator classes and smart pointer classes = Check (Curr, "dereference past End"); return (*P) [Curr]; } std::string* operator-> () const {return & this->operator* (); }}; the arrow operator must be a member of the class. The dereference operator is usually also a member function of a class, although it is not required. The return value is a reference or pointer to a very large string, respectively, because a strblobptr intelligent binding Strblob Object (the constructor accepts non-const) and most other operators, we can make operator* do the operations we specify. But the arrow operator can never discard the most basic meaning of the member. We can change the object from which the arrow gets the member, and the fact that the arrow goes back to the member will never change. The overloaded arrow operator must return a pointer to a class or an object of a class that has a custom arrow operator.
If the class overloads the function call operator, we can use the class as if it were a function. Because such classes can store state at the same time, they are more flexible than functions. The function call operator must be a primitive function. A class can define several different versions of the call operators, which should differ from one another on the number or type of arguments. If a class defines a call operator, the object of that class is called a function object. Class Printstring{public:printstring (Ostream &o = cout, char c = "): OS (o), Sep (c) {} void operator () (CO NST string &s) const {OS<<S<<SEP;} Private:ostream &os; char Sep;}; Function objects are often used as arguments to generic algorithms. For_each (Vs.begin (), Vs.end (), printstring (Cerr, ' \ n ')); the third parameter of For_each is a temporary object of type printstring, where we initialize the object with Cerr and newline characters. When the program calls For_each, none of the elements in VS will be printed to Cerr, and the elements are separated by a newline character.
When we have written a lambda expression, the compiler translates the expression to an unnamed object. The lambda expression contains an overloaded function-dispatching operator. By default, a lambda expression cannot change the variable that he captures, so by default the function call operator in a class that is generated by Lambda is a const member function. If it is declared mutable, the calling operator is not Const.
The captured variables are copied into the lambda expression, so the class that the lambda expression produces must establish the corresponding data member for each value capture variable, and build the constructor. Initializes a data member with a value class that uses the captured variable. such as: Auto WC = find_if (Words.begin (), Words.end (), [SZ] (const string &a) {return a.size () >=sz;}); The class generated by the lambda expression is: Class sizecomp{Sizecomp (size_t N): Sz (n) {} bool Operator () (const string &s) const {R Eturn s.size () >= sz; }private:size_t sz;}; Auto WC = find_if (Words.begin (), Words.end (), Sizecomp (SZ)); The class produced by the lambda expression does not contain a default constructor, an assignment operator, and a default destructor; Does it contain a default copy/ The move constructor is usually determined by the type of data being captured.
function objects defined by the standard library: are in the functional header file. Are all templates, should be plus<type> The following only gives the name arithmetic: Plus,minus, multiplies, divides, modulus, negate relations: equal_to, not_equal_to, Greater, greater_equal, less, less_equal logic: Logical_and,logical_or,logicla_not
function objects using the standard library in the algorithm: Vector<string * > Nametable;sort (Nametable.begin (), Nametable.end (), [] (string *a, String *b) {RET Urn a< b;}); errors, which have a relationship between pointers, will produce undefined. Sort (Nametable.begin (), Nametable.end (), less<string*> ());//Correct association container uses less<key_type> to sort elements, So we can define a set of pointers or use pointers as key values in a map without directly declaring less
C + + Callable objects: functions, function pointers, lambda expressions, bind-created objects, and classes that overload the function call operator. As with other objects, callable objects have types as well. Different types may have the same invocation form. In cases where several callable objects share the same invocation form, sometimes we want to think of them as having the same type. We can use function to solve this problem in the functional header file.
Operation of function: function<t>f; F is an empty function used to store callable objects, which should be called in the same form as the function type T function<t> f (nullptr); Explicitly constructs an empty functionfunction<t> f (obj); Stores a copy of the callable object obj in F. F is used as a condition: When F contains a callable object, it is true; F (args) calls the object in F, and the argument is the member type of the callable object that args defines as function<t> Result_typ E The type returned by the callable object of the function type Argument_type the type defined when T has one or two arguments. If T has only one argument, then Agreement_type is a synonym for that type, and if there are two arguments, First_argument_type and second_argument_type represent the types of the two arguments respectively.
A function is a template that needs to provide additional information that a function type can represent in the form of an object's invocation. Function<int (Int,int) > We declare a function type that can represent a callable object that accepts two int and returns an int. Function<int (int,int) >f1 =add; function pointer Function<int (int,int) >f2 = Divide (); Object of the Function object class Function<int (int,int) >f3 = [] (int i, int j) {return i* J;};
Map<string, function<int (int, int) >> Binops = {"+", add}, {"-",std::minus<int> ()}, {"/", divi De ()}, {"*", [] (int i, int j) {return i* J;}}, {"%", mod}};binops["+"] (10,5);
We can not (directly) put the overloaded function name into the object of function type, solve two semantics, one way is to use the function pointer, one is to use the lambda expression int add (int i, int j) {return i + j;} Sales_data Add (const sales_data&, const sales_data&); map<string, Function<int (int,int) >> binops; Binops.insert ({"+", add}); Error: Which add?
Int (*FP) (int,int) = Add;binops.inset ({"+", FP});
Binops.insert ({"+", [] (int a, int b) {return add (b);}});
Conversion constructors and type conversion operators collectively define class-type conversions, which are sometimes referred to as user-defined class-type conversions. is responsible for replacing a class type with another type. The operator type () const, which can be defined for any type (except void), as long as the type can be the return type of the function. Therefore, we do not allow the conversion of arrays or functions, but allow conversion to pointers (including array pointers and function pointers) or references. The type conversion operator has no explicit return type, no formal parameter, and must be a member function. and is generally a const member. Class Smallint{public:smallint (int i = 0): Val (i) {if (i<0 | | i>255) Throw STD::OUT_OF_ Range ("Bad SmallInt value"); } operator Int () const{return Val;} private:std::size_t Val;};
The compiler can only perform one user-defined type conversion at a time. However, implicit user-defined type conversions can be placed before or after a standard (built-in) type conversion and used with it. The type conversion operator is implicitly executed. Typically, a class rarely provides a type conversion operator. In most cases, if you convert automatically, you may be surprised by the user. However, there is an exception: for classes, it is more common to define type conversions to bool. In earlier versions, if you defined a type conversion of bool. It often encounters a problem: int i = 42;cin<<i; This code attempts to effect the output operator on the input stream. Because the IStream itself does not have a definition << so the code will produce an error. However, the code can convert CIN to bool using the IStream bool type reload operator, which is then promoted to int and used as the left operand of the built-in left-shift operator. Explicit class-Type conversion operators: class smallint{public:explicit operator int () const{return val;}}; SmallInt si = 3;si + 3; Error static_cost<int> (SI) + 3; Correct when the type conversion operator is explicit, we can also perform a type conversion, but it must be done by explicitly forcing the type conversion. There is an exception to this rule, however, when an expression is used as a condition, the compiler automatically applies explicit type conversions to it: if, while and do statements, conditional expressions for the header of a statement, operands of logical operators, conditional expressions for conditional operators. In an early-riser version, IO defines the conversion rules you want to void* in order to avoid over-conversion. But in the new standard, IO is defined as the conversion of explicit to bool type conversions are usually used in the conditional section, so operator bool is generally defined as explicit
If your class contains one or more type conversions, you must ensure that only one conversion exists between the class type and the destination type. Otherwise, there is a good chance of generating two semantics. In general, do not define the same type conversions for your class, nor do you define conversions for arithmetic types of two and more than two transformation sources or conversion targets in a class. In one example, we define two ways to convert B to a: a type conversion operator using B in, another constructor with a B parameter with a and if a set of type conversions is defined, their transformation source (or conversion destination) type itself can be associated with other type conversions. It will also produce two questions of righteousness. The simplest and most disturbing example is that a constructor that defines multiple parameters in a class is an arithmetic type, or the target is. struct a{a (int = 0); A (double); operator int () Const;operator double () const; void f (long double); A a;f (a)//; the translation of the above ambiguity has two semantics, the root cause is that they require the same level of standard type conversions. When we use a user-defined type conversion, if the conversion process contains a standard type conversion, the level of the standard type conversion determines the process by which the compiler chooses the best match short//to promote short to int over to convert short to Doublea A3 (s); with a::a (int), we should try to avoid defining type conversion functions and limiting as much as possible the "apparently correct" non-display constructors, except for those that show the conversion to type bool.
If we need to use constructors or force a type conversion class to change the type of an argument when calling an overloaded function, this usually means that there is a shortage of program design.
When an overloaded function is called, if additional standard type conversions are required, the level of the conversion is only useful if all feasible functions request the same user-defined type conversion. If there is more than one user-defined type conversion required, the invocation is ambiguous. Overloaded functions must use the same type conversion. Instead of considering the priority of the conversion.
Overloaded operators are also overloaded functions. Therefore, the common function matching rules are also used. For overloaded operators, however, we cannot differentiate between members or non-members or built-in versions. In addition, if the left operand is a class type, the overloaded version of the operator defined in the class is also contained within the candidate function. If our team is the same class that provides conversion targets for type conversions of arithmetic types, and also provides overloaded operators, you will encounter the two semantic problem of overloaded operations with built-in operators. Whether to use + is to use an overloaded version or to convert one side.
C++11 (13): Overloaded operations and type conversions