Document directory
- Left and right
- C ++ left value and right value Extension
- Use of function objects
- Custom function object
Index
- Data Structure weapon-private data center STL (on)
- Data Structure weapon-private data center STL (medium)
- Data Structure tool-private data center STL (lower)
This article http://www.cnblogs.com/daoluanxiaozi/archive/2012/12/02/confidential-stl.html due to a serious violation of the "blog garden home page" related rules, so the author changed the "private house STL" series of every article are integrated into the blog garden, cancel external links. In addition, considering the length of space,This series of articles will be divided into upper, middle, and lower. This article is "Data Structure weapon's private house STL (lower)",Last article. If you like it, run the following command :-)
This series of articles is suitable for beginners who want to analyze STL and want to review STL.
- Left and right values of STL in private rooms
- Function object of private data center STL
- Function adapter of STL in private rooms
- Private room STL iterator
Left and right values of STL in private rooms
The left and right values are not specific to the content in STL. They discovered the knowledge rule vulnerability of the author C/C ++ in the process of accessing STL.
Left and right
The left value (lvalue) is the value on the left of the equal sign, and the right value (Rvalue) is the value on the right of the equal sign. the right value must be placed on the right of the equal sign, however, the left value can be either left or right. Then the value (Equality), String, constant, can only be used as the right value, the right value must not be placed on the left side of the equal sign.
100 = 60;/* The value is the right value, which is invalid */'A' = 'a';/* The character is the right value, which is invalid */const int A = 1; A = 100;/* A is a constant and belongs to the right value. It is invalid */
Variable, reference as the left value, both on the left side of the equation and on the right side of the equation.
Int A, B; A = 2;/* A left value, 2 right value. */B = A;/* left value of B, left value of. */INT & C = A;/* The left value of A and the left value of C. */
In particular, there are two types of auto-increment: I ++ and I, but there is a difference between the two, allowing me to expand their operations using C:
I ++ operations:
{int temp = i;++i;return temp;}
++ I operations:
{i = i + 1;return i;}
Therefore, I ++ = 1 is invalid because I ++ returns a temporary value instead of I itself. To eliminate ambiguity, I ++ resolutely returns the right value, that is to say, it can only be placed on the right side of the equation. While ++ I = 1 is legal. From the expanded operation above, ++ I returns itself, so it is a left value, it can be on the left or right of the equation.
C ++ left value and right value Extension
Extends to the operator overloading of classes. Let's first assume a class node,
Class node {public: node (INT Nage = 0) {m_nage = Nage;} int getage () const {return m_nage;} node operator ++ (int n) /* I ++ */{node temp = * This; m_nage ++; return temp;} node & operator ++ () /* ++ I * // * Do you know why the reference is returned? */{M_nage ++; return * This;} node & operator = (int n) {m_nage = N; return * This;} PRIVATE: int m_nage ;};
C ++ stipulates that node & operator ++ () is the overload prefix auto-increment operator (++ I), while node operator ++ (INT N) is the auto-increment operator (I ++) with a heavy suffix ). It is found that the reload prefix auto-increment operator returns a reference, while the reload suffix auto-increment operator returns a temporary variable. In other words, if there is a Node object, I want ++ node (prefix) to return the left value, and node ++ (suffix) to return the right value. Hope:
+ + Node = 1;/* Valid, ++ node return value as the left value */node ++ = 1;/* invalid, node ++ return value as the left value */
However, the overload operator is originally used to change the operator's behavior, so the above behavior is permitted by the compiler. But there is no syntax problem, but there is a serious logical vulnerability. ++ Node = 1; it does change the content of node, but node ++ = 1; failed because the "= 1" operation is executed on temp, therefore, node ++ = 1; after execution, the node content is changed to the previous value + 1 instead of equal to 1.
... Node (23);/* node. m_nage the initial value is 23. */Node ++ = 1; cout <"Node ++ = 1; after execution, node. m_nage = "<node. getage () <Endl; ++ node = 1; cout <"++ node = 1; after execution, node. m_nage = "<node. getage () <Endl ;......
Node ++ = 1; after execution, node. m_nage = 24
+ + Node = 1; after execution, node. m_nage = 1
Press any key to continue...
Node ++ = 1; after execution, the node is not "= 1. Can it be the opposite? "I want it to return the left value "! Of course, there is no problem, as long as you modify its behavior.
...... Node & operator ++ (int n)/* I ++ * // * modifies the behavior of I ++, let it also return the left value */{// node temp = * This; // m_nage ++; // return temp; m_nage ++; return * This ;}......
Node ++ = 1; after execution, node. m_nage = 1
+ + Node = 1; after execution, node. m_nage = 1
Press any key to continue...
However, we recommend that you do not do this because it may cause confusion.
In summary, the left value can appear on the left and right of the equation. The right value can only appear on the right. ++ I returns the left value and I ++ returns the right value, which is why the reference (left value) is returned when the prefix auto-increment operator is overloaded.
After this article is completed
Spoof http://www.daoluan.net/
Function object of private data center STL
One-sentence function object: The secret of a function object (also called a function imitation) is not enough to surprise you. It is an object of A Class (struct) That reloads the "()" operator. The implementation is simple: declare a class (struct) and reload the "()" operator.
PS: you also know the definition of function objects. The following function objects sometimes refer to function objects, and sometimes to classes (struct) corresponding to function objects ).
STL has built-in function objects, such as arithmetic function objects (+,-, and so on) and relational operation function objects (= ,! , Etc.), logical operation class function objects (&, |, etc.), see; Operator.
Use of function objects
Function objects are rarely directly applied. However, to work with the content of the next adapter (next blog post), perform a simple test:
cout << "10 + 10 = " << plus<int>()(10,10) << endl;cout << "10 - 10 = " << minus<int>()(10,10) << endl;cout << "10 == 10 ? " << equal_to<int>()(10,10) << endl;
10 + 10 = 20
10-10 = 0
10 = 10? 1
Press any key to continue...
In plus <int> () (10, 10);, the first bracket is a temporary object to generate struct plus, and the second bracket calls the () function that is overloaded inside struct plus. The above is an elegant writing method, which can also be popular:
plus<float> opplus;cout << "10.0 + 10.1 = " << opplus(10.0,10.1) << endl;
Here is the overall picture of struct Plus, which is simple:
template<class _Ty>struct plus: public binary_function<_Ty, _Ty, _Ty>{// functor for operator+_Ty operator()(const _Ty& _Left, const _Ty& _Right) const{// apply operator+ to operandsreturn (_Left + _Right);}};
Custom function object
In some cases, the built-in function objects cannot meet our requirements. We need to customize a function object to deal with the current problem. STL rules: all possible mona1 functions should inherit unary_function (unary: mona1), and all possible binary functions should inherit binary_function (Binary: Binary ). For example, we define a square arithmetic function object, which is very easy. Just modify the internal execution of the object as shown in the figure above:
template<class _Ty>struct square: public unary_function<_Ty, _Ty>{_Ty operator()(const _Ty& Arg) const{return (Arg * Arg);}};......cout << "10^2 = " << square<int>()(10) << endl;......
10 ^ 2 = 100
Press any key to continue...
After this article is completed
Spoof http://www.daoluan.net/
Function adapter of STL in private rooms
This article describes how the function adapter works through simple examples.
Function objects are directly applied in a small number of places. It is used together to implement some algorithms (as algorithm parameters), so there is a function adapter. Because function objects can almost implement the expressions we want, some algorithms can indeed obtain the expected results (put the expectation in the expression) through function objects.
Show a piece of code:
/* From C ++ standard STL. */......Int Ia [] = {11, 12, 13, 1, 2, 3, 4, 5, 6, 7}; vector <int> IV (IA, Ia + 10); cout <count_if (IV. begin (), IV. end (), bind2nd (less <int> (), 10) <Endl ;......
7
Press any key to continue...
We can try to expand the count_if () function step by step,
/* From C ++ standard STL. */Template <class _ init, class _ Pr> inlinetypename iterator_traits <_ init>: difference_typecount_if (_ init _ first, _ init _ last, _ Pr _ Pred) /* here. */{// Count elements satisfying _ predreturn _ count_if (_ checked_base (_ first), _ checked_base (_ last), _ Pred);}/* count_if (). */Template <class _ init, class _ Pr> inlinetypename iterator_traits <_ init>: difference_type_count_if (_ init _ first, _ init _ last, _ Pr _ Pred) {// count elements satisfying _ pred_debug_range (_ first, _ last); _ debug_pointer (_ Pred); typename iterator_traits <_ init >:: difference_type _ COUNT = 0; (; _ first! = _ Last; ++ _ First) if (_ PRED (* _ First) ++ _ count; Return (_ count );}
Here, _ Pred is the function object binder2nd, which is constructed in return (STD: binder2nd <_ FN2> (_ FUNC, _ Val); statement, at the same time, it reloads the "()" operator. Let's take a look at bind2nd and binder2nd:
/* From C ++ standard STL. * // Template class binder2ndtemplate <class _ FN2> class binder2nd: Public unary_function <typename _ FN2: first_argument_type, typename _ FN2 :: result_type> {// functor adapter _ FUNC (left, stored) Public: typedef unary_function <typename _ FN2: first_argument_type, typename _ FN2: result_type> _ base; typedef typename _ base: argument_type; typedef typename _ base: result_type; binder2nd (const _ FN2 & _ FUNC, const typename _ FN2: Required & _ Right ): OP (_ FUNC), value (_ Right) {// construct from functor and right operand} result_type operator () (const argument_type & _ left) const {// apply functor to operandsreturn (OP (_ left, value);} result_type operator () (argument_type & _ left) const {// apply functor to operandsreturn (OP (_ left, value);} protected: _ FN2 op; // The functor to applytypename _ FN2: second_argument_type value; // The right operand}; // template function bind2ndtemplate <class _ FN2, class _ ty> inlinebinder2nd <_ FN2> bind2nd (const _ FN2 & _ FUNC, const _ ty & _ Right) {// return a binder2nd functor adaptertypename _ FN2: second_argument_type _ Val (_ right); Return (STD :: binder2nd <_ FN2> (_ FUNC, _ Val ));}
Bind2nd () isAuxiliary FunctionsTo make it easier to use binder2nd (the real main character.
Count_if (IV. begin (), IV. end (), bind2nd (less <int> (), 10); less <int> () the constructor of the binder12 function object is used as its internal operation member (it is a function object) _ FN2 op;
When processing each element, the count_if () function actually calls the "()" operator of binder2nd to reload the function, this function calls the "()" operator of its internal operation member OP (which is also a function object less <int>) to overload the function. As a result, the matching is successful. Other function adapters are similar.
In summary, count_if () calls the bind2nd () function. The bind2nd () function actually generates the binder2nd function object and returns it to count_if () as the parameter. count_if () call the more underlying function _ count_if.
We can know from the source code of count_if () that count_if (IV. begin (), IV. end (), bind2nd (less <int> (), 10); can also be rewritten,
bool less10(int i){if(i<10)return true;return false;}......int ia[] = {11,12,13,1,2,3,4,5,6,7};vector<int> iv(ia,ia+10);cout << count_if(iv.begin(),iv.end(),less10) << endl;......
In this case, _ Pred in count_if () is a function pointer. Therefore, function objects can still be replaced with common function pointers.
Function adapter implemented by STL: http://www.cplusplus.com/reference/std/functional/
After this article is completed
Spoof http://www.daoluan.net/
Private room STL iterator
Iterator has made great contributions to STL's outstanding performance.
A Data Element has multiple representation modes, including entity (itself), pointer, and reference. In addition, there are two types related to the data element attribute: spacing (that is, the relationship between the two data elements) and its mobile features and implementation methods. The iterator contains the descriptions of the above five elements, so the iterator can fully represent and manipulate a data element. The iterator itself can only represent operational data elements, but it does not include any data metadata. It is like the relationship between the TV and the remote control. The remote control (iterator) can only switch between different channels, however, it does not include television sets (data elements ).
Here, the iterator is not described separately. It is not a single individual. It is placed in the big framework to better reflect its role and execution mechanism.
The iterator will be defined in STL:
Template <class _ ty>/* _ TY: node element type. */Struct container_iterator {/* description of the five elements that are first related to the data element. */...... Typedef identifier <_ ty> iterator; typedef identifier; typedef _ ty value_type; typedef node <_ ty> * link_type; typedef size_t size_type; typedef ptrdiff_t difference_type; link_type node; /* iterator constructor. */...... Container_iterator (link_type X): node (x ){}......... /* iterator behavior * // * +,-, ++, --, + =,-=,->, &, [], and other operators to obtain required values */};
-
Iterator_op
It can be seen that the iterator does not contain data entities, but can only represent and operate data entities. Because of the implementation of the container_iterator control above, when there is container_iterator at hand, you can "* ite" to get the reference of the data element, and you can "ite->" to get the pointer of the data element, "++" can take another ite step forward automatically, and "--" can take another ite step back...
Through templates, The iterator can serve any data element. An interesting part is the constructor of the iterator:
container_iterator(link_type x):node(x){}
In element operations of container (as shown below), the pointer to the data element is often directly intercepted,In this case, the function of this operation may need to return the container_iterator type,Instead of returning a pointer to the data element (this method is not the same, it is too complex), so it will temporarily construct (call the constructor of the iterator) An iterator as the return value.
Meaning:
Class node {public: node (INT Nage = 0) {m_nage = Nage ;}...... PRIVATE: int m_nage;}; node Foo (int I) {return I;/* returns an int directly, but node has a node (INT) constructor, therefore, a Node object is constructed temporarily and returned. */} Int main () {node I = Foo (2); Return 0 ;}
The following is the container:
Template <class _ ty, class alloc>/* T: Type of the node element. */Class container {/* container Data Structure */typedef container_iterator <_ ty> iterator; typedef const container_iterator <_ ty> const_iterator; typedef node <_ ty> * link_type; typedef _ ty value_type; typedef node <_ ty> * link_type; typedef size_t size_type; typedef ptrdiff_t difference_type; private: link_type head;/* Header pointer. */Link_type tail;/* is the pointer. */Iterator begin (); iterator end (); bool empty (); size_type size () const. Push_front, push_back, pop_front, pop_back, insert, earse, etc. */Iterator insert (const _ ty & X); iterator earse (iterator position); void push_back (const _ ty & X); void push_front (const _ ty & X ); void pop_back (); void pop_front ();......};
Most of the functions implemented inside the container are the operation functions of the elements. They fully utilize container_iterator, including the control of various elements implemented inside the container_iterator (++, --, *,->, and so on ).
Container and container_iterator are combined in this way. There is a treasure of Library in STL: algorithm.In general algorithms,The iterator is indispensable. How can it be universal? If different containers correspond to different iterators, is it necessary to implement versions of multiple iterators for an algorithm? No, no, this isGeneral ProgrammingAccording to the introduced iterator (the General STL algorithm uses the iterator as the parameter) to export the corresponding iterator type. Take the simplest find () algorithm as an example: the type of the iterator will be pushed through _ init _ first, and the type of the iterator will be pushed and the corresponding container will be exported.
/* From C ++ standard library. */Template <class _ init, class _ ty> inline_init find (_ init _ first, _ init _ last, const _ ty & _ Val) {// find first matching _ val_assign_from_base (_ first, _ Find (_ checked_base (_ first), _ checked_base (_ last), _ Val )); return (_ First);} template <class _ init, class _ ty> inline_init _ Find (_ init _ first, _ init _ last, const _ ty & _ Val) {// find first matching _ val_debug_range (_ first, _ last); For (; _ first! = _ Last; ++ _ First) if (* _ first = _ Val) break; Return (_ First );}
We can see that the performance of the iterator in the algorithm is ++, --, = ....
Therefore, the iterator and Algorithm Module are combined. The iterators, containers, and Algorithms in STL are fully developed, working together on the whole, and there is a lot of detail in their respective roles. Wonderful! Wonderful!
After this article is completed
Spoof http://www.daoluan.net/