C + + primer notes Chapter 13 copy control

Time:2021-3-1
  1. Copy and move constructorsDefines what to do when initializing this object with another object of the same type.Copy and move assignment operatorsDefines what to do when an object is assigned to another object of the same type.DestructorDefines what to do when an object of this type is destroyed. We call these operationsCopy control operation

  2. If the first parameter of a constructor is a reference to its own class type and any additional parameter has a default value, the constructor iscopy constructor

  3. The first argument to a copy constructor must be a reference type. Although we can define an acceptable nonconstReference, but this parameter is almost always oneconstReference to.

  4. Copy constructors are implicitly used in several cases. Therefore, copy constructors should not normally be explicit.

  5. If we don’t define a copy constructor for a class, the compiler defines one for us. Unlike the composite default constructor, the compiler synthesizes a copy constructor for us even if we define other constructors.

  6. In general, a composite copy constructor copies the members of its parameters one by one to the object being created. The compiler copies each non static member from the given object to the object being created in turn. For some classes, the composite copy constructor is used to prevent us from copying objects of that class type.

  7. The type of each member determines how it copies: members of a class type are copied using its copy constructor; members of a built-in type are copied directly. Although we can’t copy an array directly, the composite copy constructor copies members of an array type element by element. If the array element is of class type, the copy constructor of the element is used to copy it.

  8. When using direct initialization, we actually require the compiler to use ordinary function matching to select the constructor that best matches the parameters we provide. When we usecopy initialization The compiler is required to copy the right-hand operation object to the object being created, and to perform type conversion if necessary.

  9. Copy initialization is usually done using the copy constructor. However, if a class has a move constructor, copy initialization sometimes uses the move constructor instead of the copy constructor.

  10. Copy constructors are used (copy initialization occurs) in the following cases:

    • Copy initialization (define variables with =)

    • Pass an object as an argument to a formal parameter of a non reference type

    • Returns an object from a function whose return type is not a reference type

    • Initializes elements in an array or members in an aggregate class with a curly bracket list

    • Some class types also use copy initialization for the objects they assign.(when initializing a standard library container or calling its insert / push operation, the container will copy and initialize its elements.)

  11. Copy constructors are used to initialize type parameters of non reference classes, which explains why the parameters of copy constructors themselves must be reference types.

  12. If we want to use an explicit constructor, we must use it explicitly.

    Vector V1 (10); // correct: initialize directly
    Vector V2 = 10; // error: the constructor that accepts the size parameter is explicit
    Void f (vector); // F for copy initialization
    F (10); // error: cannot copy an argument with an explicit constructor
    F (vector (10)); // correct: construct a temporary vector directly from an int
  13. During copy initialization, the compiler can (but does not have to) skip the copy / move constructor and create objects directly. That is, the compiler is allowed to rewrite the following code:

    string null_ Book = "9-999-9999-9"; // copy initialization
    string null_ Book ("9-999-99999-9"); // the compiler ignores the copy constructor

    However, even if the compiler skips the copy / move constructor, the copy / move constructor must exist and be accessible (for example, it cannot be private) at this program point.

  14. The arguments of the overloaded operator represent the operands of the operator. Some operators, including assignment operators, must be defined as member functions. If an operator is a member function, the left operation object is bound to the implicit this parameter. For a binary operator, such as an assignment operator, the right operands are passed as explicit parameters.

  15. The copy assignment operator takes a parameter of the same type as its class:

    class Foo
    {
    public:
    	Foo & operator = (const foo &); // assignment operator
    	// ...
    };
  16. An assignment operator should normally return a reference to the operands to its left.

  17. As with copy constructors, if a class does not define its own copy assignment operator, the compiler generates aComposite copy assignment operator

  18. Similar to the copy constructor, for some classes, the composite copy assignment operator is used to prohibit the assignment of objects of this type. If the copy assignment operator is not for this purpose, it will assign each non static member of the right operands to the corresponding members of the left operands. This is done by the copy assignment operator of member type. For members of array type, the array elements are assigned one by one. The composite copy assignment operator returns a reference to the operand to its left.

    //Equivalent to composite copy assignment operator
    Sales_data& Sales_data::operator=(const Sales_data &rhs)
    {
    	bookNo =  rhs.bookNo ; // call string:: operator=
    	units_ sold =  rhs.units_ Solid; // use the built-in int assignment
    	revenue =  rhs.revenue ; // use the built-in double assignment
    	Return * this; // returns a reference to this object
    }
  19. The destructor releases the resources used by the object and destroys the non static data members of the object. A destructor is a member function of a class. Its name consists of a wavy sign followed by the class name. It has no return value and does not accept parameters:

    class Foo
    {
    public:
    	~Foo(); // destructor
    	// ...
    };
  20. The destructor cannot be overloaded because it does not accept arguments. For a given class, there is only one destructor.

  21. A destructor has a body and a destructor. In a destructor, the body of the function is executed first, and then the members are destroyed. Members are destroyed in the reverse order of initialization.

  22. In a destructor, the destructed part is implicit. What happens when a member is destroyed depends entirely on the type of the member. Destroying a member of a class type requires the member’s own destructor. Built in types have no destructors, so there’s nothing to do to destroy built-in type members.

  23. Implicitly destroying a member of a built-in pointer type does not delete the object it points to.

  24. Whenever an object is destroyed, its destructor will be called automatically

    • Variables are destroyed when they leave their scope
    • When an object is destroyed, its members are destroyed
    • When a container (whether a standard library container or an array) is destroyed, its elements are destroyed
    • Dynamically allocated objects are destroyed when the delete operator is applied to the pointer to them
    • For a temporary object, it is destroyed at the end of the full expression that created it
  25. When a reference or pointer to an object leaves the scope, the destructor does not execute.

  26. Similar to copy constructors and copy assignment operators, for some classes, composite destructors are used to prevent objects of that type from being destroyed. If this is not the case, the body of the composite destructor is empty.

    //The following code fragment is equivalent to sales_ Composite destructor of data
    class Sales_data
    {
    public:
    	//Members are automatically destroyed, and nothing else needs to be done
    	~Sales_data() { }
    	//Definition of other members, as before
    };
  27. The destructor body itself does not directly destroy members. Members are destroyed in an implicit deconstruction stage after the body of the destructor. In the whole process of object destruction, the destructor body is used as another part besides the member destruction step.

  28. When we decide whether a class needs to define its own version of the copy control member, a basic principle is to first determine whether the class needs a destructor. In general, the need for destructors is more obvious than for copy constructors or assignment operators.If a class needs a custom destructor, it almost certainly needs a custom copy assignment operator and a copy constructor

  29. When deciding whether a class wants to define its own version of copy control members, the second basic principle is:If a class needs a copy constructor, it almost certainly needs a copy assignment operator. vice versa——If a class needs a copy assignment operator, it almost certainly needs a copy constructor as well. However, whether you need to copy the constructor or copy the assignment operator does not necessarily mean that you also need the destructor.

  30. We can define copy control members as=defaultTo explicitly require the compiler to generate a composite version.

  31. When we use=defaultWhen modifying a member’s declaration, the composite function is implicitly declared inline (just like any other member function declared within a class). If we don’t want the synthesized member to be an inline function, we should only use it for the out of class definition of the member=default

    class Sales_data
    {
    public:
    	//Copy control members; using default
    	Sales_data() = default;
    	Sales_data(const Sales_data&) = default;
    	Sales_data& operator=(const Sales_data &);
    	~Sales_data() = default;
    	//Definition of other members, as before
    };
    Sales_data& Sales_data::operator=(const Sales_data&) = default;
  32. We can only use it for member functions that have a composite version=default(that is, the default constructor or copy control member).

  33. We can define the copy constructor and the copy assignment operator asDeleted functionTo prevent copying: we declare them, but we cannot use them in any way. Add the=deleteTo point out that we want to define it as deleted:

    struct NoCopy
    {
    	Nocopy() = Default; // use the synthesized default constructor
    	Nocopy (const nocopy &) = delete; // prevent copy
    	Nocopy & operator = (const nocopy &) = delete; // block assignment
    	~Nocopy() = Default; // use synthesized destructor
    	//Other members
    };
  34. And=defaultDifferent,=deleteIt must appear when the function is first declared, and the difference logically coincides with the meaning of these declarations. A default member affects only the code generated for that member, so=defaultNot until the compiler generates code. The compiler, on the other hand, needs to know that a function is deleted in order to prohibit attempts to use it.

  35. And=defaultAnother difference is that we can specify the=delete(we can only use it for default constructors or copy control members that the compiler can compose=default)。 Although the main use of delete functions is to prohibit copying control members, it is sometimes useful when we want to guide the function matching process.

  36. For a type with destructor removed, the compiler will not allow variables of that type to be defined or temporary objects of that class to be created. Moreover, if a class has a member type and the destructor is deleted, we cannot define the variable or temporary object of the class.

  37. For the type of destructor deleted, although we cannot define variables or members of this type, we can dynamically allocate objects of this type. However, you cannot release these objects:

    struct NoDtor
    {
    	Nodtor() = Default; // use composite default constructor
    	~Nodtor() = delete; // we cannot destroy objects of type nodtor
    };
    Nodtor nd; // error: the destructor of nodtor is deleted
    Nodtor * P = new nodtor(); // correct: but we can't delete P
    Delete p; // error: the destructor of nodtor is deleted
  38. You cannot define a variable of a destructor deleted type or release a pointer to a dynamically allocated object of that type.

  39. For some classes, the compiler defines these synthesized members as deleted functions:

    • If the destructor of a member of a class is deleted or inaccessible (for example, private), the composite destructor of the class is defined as deleted.
    • If the copy constructor of a member of a class is deleted or inaccessible, the composite copy constructor of the class is defined as deleted. If the destructor of a member of a class is deleted or inaccessible, the copy constructor of the class composition is also defined as deleted.
    • If the copy assignment operator of a member of a class is deleted or inaccessible, or the class has aconstThe composite copy assignment operator of the class is defined as deleted.
    • If the destructor of a member of a class is deleted or inaccessible, or the class has a reference member, it does not have an in class initializer, or the class has aconstMember, which does not have an in class initializer and whose type does not explicitly define a default constructor, then the default constructor of the class is defined as deleted.
  40. In essence, the meaning of these rules is: if a class has data members that cannot be constructed, copied, assigned or destroyed by default, the corresponding member function will be defined as deleted.(essentially, when it is impossible to copy, assign, or destroy a member of a class, the composite copy control member of the class is defined as deleted.)

  41. Because trying to access an undefined member will result in a link error by declaring (but not defining)privateWe can prevent any attempt to copy this type of object in advance: the user code that attempts to copy the object will be marked as an error in the compilation phase; the copy operation in the member function or friend function will cause link time error.

  42. Classes that want to prevent copying should use=deleteTo define their own copy constructors and copy assignment operators instead of declaring them asprivateYes.

  43. In general, a class that manages resources outside of a class must define a copy control member. To define these members, we must first determine the copy semantics of this type of object. In general, there are two options: you can define copy operations to make the class behave like a value or like a pointer.

    • Class behaves like a value, meaning that it should also have its own state. When we copy an object with image value, the copy and the original object are completely independent. Changing the copy has no effect on the original object and vice versa.
    • Classes that behave like pointers share state. When we copy an object of this kind, the copy uses the same underlying data as the original object. Changing the copy also changes the original object, and vice versa.
  44. Key concept: assignment operator

    When you write assignment operators, there are two things to remember:

    • If you assign an object to itself, the assignment operator must work correctly.
    • Most assignment operators combine the work of destructors and copy constructors.

    When you write an assignment operator, a good pattern is to first copy the right-hand operands into a local temporary object. When the copy is completed, it is safe to destroy the existing members of the left side operation object. Once the resources of the left-hand operation object are destroyed, it is only left to copy the data from the temporary object to the members of the left-hand operation object.

    HasPtr& HasPtr::operator=(const HasPtr &rhs)
    {
    	auto newp = new string(* rhs.ps ); // copy the underlying string
    	Delete PS; // release old memory
    	PS = NEWP; // copy data from the right operation object to this object
    	i = rhs.i;
    	Return * this; // returns this object
    }

    It’s wrong to write assignment operators like this

    HasPtr& HasPtr::operator=(const HasPtr &rhs)
    {
    	Delete PS; // releases the string pointed to by the object
    	//If RHS and * this are the same object, we will copy the data from the released memory!
    	ps = new string(*(rhs.ps));
    	i = rhs.i;
    	return *this;
    }
  45. The best way to make a class behave like a pointer is to use theshared_ptrTo manage resources in a class. But sometimes we want to manage resources directly. In this case, useReference countIt’s very useful. Reference counting works as follows:

    • In addition to initializing objects, each constructor (except copy constructors) also creates a reference count to record how many objects share state with the object being created. When we create an object, only one object shares state, so we initialize the counter to 1.
    • Instead of assigning a new counter, the copy constructor copies the data members of the given object, including the counter. The copy constructor increments the shared counter to indicate that the state of a given object is shared by a new user.
    • The destructor decrements the counter to indicate that there is one less user in shared state. If the counter goes to 0, the destructor releases the state.
    • The copy assignment operator increments the counter of the operands on the right and decrements the counter of the operands on the left. If the counter of the left operation object changes to 0, it means that there are no users in its shared state, and the copy assignment operator must destroy the state.
  46. One way to implement shared counters is to store them in dynamic memory.

    class HasPtr
    {
    public:
        //The constructor allocates a new string and a new counter and sets the counter to 1
        HasPtr(const string &s = string()) : ps(new string(s)), i(0), use(new size_t(1)) {}
        //The copy constructor copies all three data members and increments the counter
        HasPtr(const HasPtr &p) : ps(p.ps), i(p.i), use(p.use) { ++*use; }
        HasPtr &operator=(const HasPtr &);
        ~HasPtr();
    
    private:
        string *ps;
        int i;
        size_ T * use; // used to record how many objects share * PS members
    };
    HasPtr &HasPtr::operator=(const HasPtr &rhs)
    {
        ++* rhs.use ; // increment the reference count of the right operands
        If (- - * use = = 0) // then decrement the reference count of this object
        {
            Delete PS; // if there are no other users
            Delete use; // release the assigned members of this object
        }
        ps =  rhs.ps ; // copy data from RHS to this object
        i = rhs.i;
        use = rhs.use;
        Return * this; // returns this object
    }
    HasPtr::~HasPtr()
    {
        If (- - * use = = 0) // if the reference count becomes 0
        {
            Delete PS; // free string memory
            Delete use; // release counter memory
        }
    }
  47. In addition to defining copy control members, the class that manages resources usually defines a function called swap. For classes that are used with reordering algorithms, it is important to define swap. Such algorithms call swap when they need to exchange two elements.

  48. If a class defines its own swap, the algorithm uses the custom version of the class. Otherwise, the algorithm uses the swap defined by the standard library.

    class HasPtr
    {
    	friend void swap(HasPtr&, HasPtr&);
    	//Definition of other members
    };
    
    //Since swap exists to optimize the code, we declare it as an inline function.
    inline void swap(HasPtr &lhs, HasPtr &rhs) 
    {
    	using std::swap;
    	swap( lhs.ps ,  rhs.ps ); // exchange pointers instead of string data
    	Swap (LHS. I, RHS. I); // exchange int members
    }
  49. Unlike copy control members, swap is not necessary. However, defining swap may be an important optimization method for the class that has allocated resources.

  50. The swap function should call swap instead of STD:: swap. There is no specific version of swap for the built-in type, and the call to swap will call the standard librarystd::swap. However, if a member of a class has its own type specific swap function, callstd::swapIt’s wrong.

  51. If there is a type specific version of swap, the swap call matches it. If no type specific version exists, thestdVersion in (assuming that there is ausingStatement).

  52. The classes that define swap usually use swap to define their assignment operators. These operators use a method calledCopy and exchangeIt’s a new technology. This technique exchanges the left-hand operation object with a copy of the right-hand operation object

    //Note that RHS is passed by value, which means that the copy constructor of hasptr
    //... copy the string in the right operation object to RHS
    HasPtr& HasPtr::operator=(HasPtr rhs)
    {
    	//Exchange the contents of left operation object and local variable RHS
    	Swap (* this, RHS); // RHS now points to the memory used by this object
    	Return * this; // RHS is destroyed, thus deleting the pointer in RHS
    }
  53. useCopy and exchangeThe automatic assignment operator of is exception safe and can handle self assignment correctly.

  54. Generally speaking, a class that allocates resources needs more copy control, but resource management is not the only reason that a class needs to define its own copy control members. Some classes also need the help of copy control members for bookkeeping or other operations.

  55. Copy assignment operators usually do the same thing in copy constructors and destructors. In this case, the public work should be done in the tool function of private.

  56. In some cases, the object is destroyed immediately after it is copied. In these cases, moving rather than copying objects can significantly improve performance.

  57. Standard library containerstringandshared_ptrClass supports both mobile and copy. IO class andunique_ptrClass can be moved but not copied.

  58. rvalue reference ——That is, the reference that must be bound to the right value. We get the right-hand reference by & & instead of & instead. Right valued references can only be bound to one object to be destroyed. Therefore, we are free to “move” a resource referenced by a right value to another object.

  59. For regular references (we can call themlvalue reference ), we cannot bind it to an expression that requires conversion, literal constant, or an expression that returns a right value. A right value reference can be bound to such an expression, but a right value reference cannot be bound directly to a left value

    int i = 42;
    Int & R = I; // correct: R refers to I
    Int & & RR = I; // error: cannot bind a right value reference to a left value
    Int & R2 = I * 42; // error: I * 42 is a right value
    Const int & R3 = I * 42; // correct: we can bind a const reference to a right value
    Int & & RR2 = I * 42; // correct: bind RR2 to the multiplication result
  60. Functions that return lvalue references, together with assignment, subscript, dereference, and pre increment / decrement operators, are examples of expressions that return lvalues. An lvalue reference can be bound to the result of such expressions. Functions that return non reference types, together with arithmetic, relational, bitwise, and post increment / decrement operators, generate right values. You can use aconstIs bound to this type of expression.

  61. The left value is persistent and the right value is transientLeft values have persistent states, while right values are either literal constants or temporary objects created during expression evaluation.

  62. Since the right value reference can only be bound to temporary objects, we know that:

    • The referenced object will be destroyed
    • There are no other users for this object

    These two features mean that code using right-hand references is free to take over the resources of the referenced object.

  63. The right value reference points to the object to be destroyed. Therefore, we can “steal” state from objects bound to right-sided references.

  64. A variable can be considered as an expression with only one operator and no operator. Variable expressions are all left valued. We can’t bind a right value reference directly to a variable, even if the variable is a right value reference type.

    Int & & rr1 = 42; // correct: literal constant is right value
    Int & & RR2 = rr1; // error: the expression rr1 is an lvalue!
  65. By calling amoveThis function is defined in the header fileutilityIn the middle.

    int &&rr3 = std::move(rr1); // ok
  66. We can destroy a moved source object or give it a new value, but we can’t use the value of a moved source object.

  67. Code that uses move should usestd::moveinstead ofmove. This avoids potential name conflicts.

  68. In addition to completing the resource move, the move constructor must also ensure that the moved source object is in such a state that it is harmless to destroy it. In particular, once the resource has been moved, the source object must no longer point to the resource being moved – the ownership of these resources already belongs to the newly created object.

    Strvec:: strvec (strvec & & S) noexcept // the move operation should not throw any exception
    //The member initializer takes over the resources in S
    : elements(s.elements), first_free(s.first_free), cap(s.cap)
    {
    	//Put s in such a state that it is safe to run the destructor on it
    	s.elements = s.first_free = s.cap = nullptr;
    }
  69. Unlike the copy constructor, the move constructor does not allocate any new memory; it takes over the memory in a given object. After taking over memory, it sets all the pointers in the given object tonullptr. This completes the move operation from the given object, which will continue to exist. Eventually, the moved source object is destroyed, which means that the destructor will run on it.

  70. Because mobile operations “steal” resources, it usually does not allocate any resources. As a result, move operations usually do not throw any exceptions. When writing a move operation that does not throw an exception, we should inform the standard library of this. Unless the standard library knows that our move constructor won’t throw an exception, it thinks that moving our class object might throw an exception, and does some extra work to deal with this possibility.

  71. One way to notify the standard library is to specify it in our constructornoexcept. In a constructor,noexceptAppears between the colon at the beginning of the parameter list and initialization list:

    class StrVec
    {
    public:
    	Strvec (strvec &) noexcept; // move constructor
    	//Definition of other members, as before
    };
    Strvec:: strvec (strvec & & S) noexcept: / * member initializer*/
    { 
        /*Constructor body*/ 
    }

    We must specify both in the declaration of the class header file and in the definition (if the definition is outside the class)noexcept

  72. Move constructors and move assignment operators that do not throw exceptions must be marked asnoexcept

  73. signnoexceptFirst, although mobile operations usually do not throw exceptions, it is also allowed to throw exceptions; second, the standard library container can guarantee its own behavior when exceptions occur. For example, vector guarantees that if we call push_ Back, the vector itself will not change. Unless vector knows that the move constructor of the element type will not throw an exception, it must use thecopy constructor If the copy is abnormal, the vector can release the newly allocated memory and return. The original elements of the vector still exist)Instead ofmove constructor If the movement is abnormal, the movement source elements in the old space have been changed, while the unstructured elements in the new space may not exist)。 If we want to move the object of our custom type instead of copying it when the vector reallocates memory, we must explicitly tell the standard library that our move constructor can be used safely. By marking the move constructor (and the move assignment operator) asnoexceptTo do this:

    StrVec &StrVec::operator=(StrVec &&rhs) noexcept
    {
    	//Direct detection and self assignment
    	if (this != &rhs)
    	{
    		Free(); // release existing elements
    		elements =  rhs.elements ; // take over resources from RHS
    		first_free = rhs.first_free;
    		cap = rhs.cap;
    		//The RHS was placed in a destructable state
    		rhs.elements = rhs.first_free = rhs.cap = nullptr;
    	}
    	return *this;
    }
  74. After the move operation, the moved source object must remain valid(Generally speaking, object validity means that it can be safely assigned a new value or used without relying on its current value)But the user can’t make any assumptions about its value.

  75. If a class defines its own copy constructor, copy assignment operator or destructor, the compiler will not synthesize the move constructor and move assignment operator for it. If a class has no move operation, the class will use the corresponding copy operation instead of the move operation through normal function matching.

  76. Only when a class does not define any copy control member of its own version, and all its (non static) data members can move construct or move assignment, the compiler will synthesize a move constructor or move assignment operator for it.

  77. Unlike copy operations, move operations are never implicitly defined as delete functions. However, if we explicitly ask the compiler to generate=defaultAnd the compiler cannot move all members, the compiler defines the move operation as a deleted function.

  78. With one important exception, when to define a composite move operation as a delete function follows the same principle as defining a delete composite copy operation

    • Different from the copy constructor, the move constructor is defined as a deleted function if a class member has defined its own copy constructor and no move constructor is defined, or if a class member has not defined its own copy constructor and the compiler cannot combine it into a move constructor. Moving assignment operators is similar.
    • If a class member’s move constructor or move assignment operator is defined as deleted or inaccessible, the class’s move constructor or move assignment operator is defined as deleted.
    • Similar to the copy constructor, if the destructor of a class is defined as deleted or inaccessible, the move constructor of a class is defined as deleted.
    • Similar to the copy assignment operator, if a class member isconstOr reference, the class’s move assignment operator is defined as deleted.
  79. If a class defines a move constructor and / or a move assignment operator, the composite copy constructor and copy assignment operator of the class are defined as deleted. Therefore, a class that defines a move constructor or move assignment operator must also define its own copy operation. Otherwise, these members are defined as deleted by default.

  80. If a class has both a move constructor and a copy constructor, the compiler uses the normalFunction matching rulesTo determine which constructor to use. The assignment operation is similar.

    StrVec v1, v2;
    V1 = V2; // V2 is an lvalue; use copy assignment
    Strvec getvec (istream &); // getvec returns a right value
    V2 = getvec (CIN); // getvec (CIN) is a right value; use move assignment
  81. If a class does not have a move constructor,Function matching rulesEnsure that objects of this type will be copied, even when we try to move them by calling move:

    class Foo
    {
    public:
        Foo() = default;
        Foo (const foo &); // copy constructor
        //Other members are defined, but foo does not define a move constructor
    };
    Foo x;
    Foo y (x); // copy constructor; X is an lvalue
    Foo Z (STD:: move (x)); // copy constructor because no move constructor is defined

    The conclusion is as followsMove the right value and copy the left value. But if the constructor is not moved, the right value is also copied.The copy assignment operator is similar to the move assignment operator

  82. Copy and exchange assignment operators and move operations: depending on the type of the argument,rhsEither the copy constructor or the move constructor is used in the copy initialization of — the left value is copied and the right value is moved. Therefore, a single assignment operator implements two functions: copy assignment operator and move assignment operator

    class HasPtr
    {
    public:
    	//Added move constructor
    	HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) { p.ps = 0; }
    	//The assignment operator is not only a move assignment operator, but also a copy assignment operator
    	HasPtr& operator=(HasPtr rhs) { swap(*this, rhs); return *this; }
    	//Definition of other members
    };
  83. to updateThree / five ruleIn general, if a class defines any copy operation, it should define all five operations.

  84. Generally speaking, copying a resource will cause some extra overhead. When this copy is not necessary, the class that defines the move constructor and move assignment operator can avoid this problem.

  85. move constructor : from scratch, there is no need to judge the self assignment;Move assignment operator: from have to have, you need to check the self assignment.

  86. Mobile iteratorAdapter: in general, the dereference operator of an iterator returns an lvalue to an element. The dereference operator of the move iterator generates a right valued reference.

  87. make_move_iteratorFunction to convert a normal iterator to a mobile iterator. This function takes an iterator parameter and returns a mobile iterator. All other operations of the original iterator work as usual in the mobile iterator. Since mobile iterators support normal iterator operations, we can pass a pair of mobile iterators to the algorithm. In particular, the mobile iterator can be passed to theuninitialized_copy

    void StrVec::reallocate()
    {
    	//Allocate twice the current size of memory space
    	auto newcapacity = size() ? 2 * size() : 1;
    	auto first = alloc.allocate(newcapacity);
    	//Moving elements
    	auto last = uninitialized_copy(make_move_iterator(begin()), make_move_iterator(end()), first);
    	Free(); // free old space
    	Elements = first; // update pointer
    	first_free = last;
    	cap = elements + newcapacity;
    }

    uninitialized_copyCalled for each element in the input sequenceconstructTo “copy” the element to the destination. This algorithm uses the dereference operator of the iterator to extract elements from the input sequence. Since we pass it a mobile iterator, the dereference operator generates a right-hand reference, which means thatconstructThe move constructor is used to construct the element.

  88. Since moving an object may destroy the original object, you can only pass the mobile iterator to the algorithm if you are sure that the algorithm will no longer access an element after assigning it a value or passing it to a user-defined function.

  89. Because a moved source object has an uncertain state, callstd::moveIt’s dangerous. When we call move, we must absolutely confirm that there is no other user for the moved source object. By carefully using move in class code, you can greatly improve performance. If you use mobile operation in ordinary user code (as opposed to class implementation code) at will, it is likely to lead to inexplicable and difficult to find errors, and it is difficult to improve the performance of the application. In addition to the implementation code of the mobile constructor and mobile assignment operator, you can only use it if you are sure that the mobile operation is necessary and safestd::move

  90. Member functions that allow movement usually use the same parameter pattern as copy / move constructors and assignment operators – one version accepts one pointerconstThe second version accepts an lvalue reference to theconstRight value reference of.

    //Suppose x is the element type
    void push_ Back (const X &); // copy: bind to any type of X
    void push_ Back (X &); // move: can only be bound to modifiable right values of type X
  91. In general, we don’t need to accept aconst X&&Or a (ordinary)X&The version of the parameter. When we want to “steal” data from an argument, we usually pass a right value reference. To achieve this, arguments cannot beconstYes. Similarly, copying from an object should not change the object. Therefore, it is usually not necessary to define an accept one (normal)X&The version of the parameter.

  92. Overloaded functions that distinguish between move and copy usually have one version that accepts oneconst T&, while another version accepts oneT&&

  93. Usually, we call a member function on an object, regardless of whether the object is an left or a right value.

    string s1 = "a value", s2 = "another";
    auto n = (s1 + s2).find('a');
    s1 + s2 = "wow!";
  94. Place one after the parameter listReference qualifier(& or &) to indicate the left / right value attribute of this. similarconstQualifiers, reference qualifiers can only be used for (non static) member functions, and must appear in both function declaration and definition.

    class Foo
    {
    public:
        Foo & operator = (const foo &) &; // only modifiable lvalues can be assigned
        //Other parameters of foo
    };
    Foo &Foo::operator=(const Foo &rhs) &
    {
    	//Perform the work required to assign RHS to this object
        return *this;
    }
  95. For & restricted functions, we can only use it for left values; for & restricted functions, we can only use it for right values.

    Foo & retfoo(); // returns a reference; the retfoo call is an lvalue
    Foo retval(); // returns a value; the retval call is a right value
    Foo I, J; // I and j are lvalues
    I = J; // correct: I is an lvalue
    Retfoo() = J; // correct: retfoo() returns an lvalue
    Retval() = J; // error: retval() returns a right value
    I = retval(); // correct: we can use a right value as the right operation object of the assignment operation
  96. A function can be used at the same timeconstAnd reference qualification. In this case, the reference qualifier must follow theconstAfter qualifier:

    class Foo
    {
    public:
    	Foo somemem() & const; // error: const qualifier must precede
    	Foo anothermem() const &; // correct: const qualifier precedes
    };
  97. Reference qualifiers can also distinguish overloaded versions. Moreover, we can combine references to qualifiers andconstTo distinguish an overloaded version of a member function.

    class Foo
    {
    public:
        Foo sorted () & &; // can be used for changeable right values
        Foo sorted() const &; // can be used for any type of foo
        //Definition of other members of foo
    private:
        vector data;
    };
    //This object is a right value, so it can be sorted in its original location
    Foo Foo::sorted() &&
    {
    	sort(data.begin(), data.end());
        return *this;
    }
    //This object is const or an lvalue. In either case, we can't sort it by its original address
    Foo Foo::sorted() const &
    {
        Foo RET (* this); // make a copy
        sort( ret.data.begin (),  ret.data.end ()); // sort copies
        Return; // returns the copy
    }

    The compiler determines which version of sorted to use based on the left / right values of the object calling sorted

    Retval(). Sorted(); // retval() is a right value, call foo:: sorted ()&&
    Retfoo(). Sorted(); // retfoo() is an lvalue. Call foo:: sorted() const&
  98. If a member function has a reference qualifier, all versions with the same parameter list must have a reference qualifier.

    class Foo
    {
    public:
    	Foo sorted() &&;
    	Foo sorted() const; // error: reference qualifier must be added
    	//Comp is a type alias for a function type
    	//This function type can be used to compare int values
    	using Comp = bool(const int&, const int&);
    	Foo sorted (COMP *); // correct: different parameter lists
    	Foo sorted (COMP *) const; // correct: there is no reference qualifier in both versions
    };

Recommended Today

Use of Android WebView (super detailed usage)

1.1 overview of WebView Android WebView is a special view on the Android platform. It can be used to display web pages. This WebView class can be used to display only one online web page in the app. Of course, it can also be used to develop browsers. The internal implementation of WebView uses WebKit […]