Details the copy and move operations of constructors and assignment operators in C++

  • 2020-05-07 20:13:46
  • OfStack

copy constructor and copy assignment operator
Starting with C++ 11,
supports two types of assignments: copy assignments and move assignments. In this article, "assignment" means copying an assignment, unless otherwise explicitly declared. Both the assignment operation and the initialization operation cause the object to be copied.
Assignment: when the value of one object is assigned to another object, the first object is copied to the second object. As a result,


Point a, b;
...
a = b;

Causes the value of b to be copied to a.
Initialization: initialization occurs when a new object is declared, an argument is passed to a function by value, or a value is returned by value from a function.
You can define the semantics of "copy" for objects of class type. For example, consider this code:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;

The previous code might mean "copy FILE1.DAT to FILE2.DAT" or "ignore FILE2.DAT and make b another handle to FILE1.DAT". You must attach the appropriate replication semantics to each class, as shown below.
By using the assignment operator operator= with a reference to the class type 1 as the parameter passed by the return type and the const reference (for example, ClassName& operator=(const ClassName& x);) .
By copying the constructor. For more information about copy constructors, see the rules for declaring constructors.
If you do not declare the copy constructor, the compiler will generate the member-wise copy constructor for you. If   does not declare the copy assignment operator, the compiler will generate the member-wise copy assignment operator for you. Declaring a copy constructor does not cancel the copy assignment operator generated by the compiler, and vice versa. If you implement one of the above, it is recommended that you implement another to make the code clear.
Member by member assignment and initialization are described in more detail.
The copy constructor takes an argument of type class-name &, where class-name is the name of the class for which the constructor is defined. Such as:


// spec1_copying_class_objects.cpp
class Window
{
public:
 Window( const Window& ); // Declare copy constructor.
 // ...
};

int main()
{
}

Description:
If possible, create the parameter const class-name & for the copy constructor of this type. This prevents the copy constructor from accidentally changing the object from which it is copied. It also supports replication from const objects.
Constructor generated by the compiler
The copy constructor generated by the
compiler (such as a user-defined copy constructor) has a single parameter type, "reference to class-name". An exception is thrown when all base and member classes have copy constructors declared to take a single argument of type const class-name &. In this case, the parameters of the copy constructor generated by the compiler are also const.
When the parameter type of the copy constructor is not const, initialization by copying the const object produces an error. The reverse is not true: if the argument is const, you can initialize by copying an object that is not const.
The assignment operator generated by the compiler follows the same pattern with respect to const. Unless the assignment operators in all base and member classes take parameters of type const class-name &, they will take a single parameter of type class-name &. In this case, the assignment operator generated by the class takes the const parameter.
Description:
When a virtual base class is initialized by a copy constructor (generated by the compiler or defined by the user), the base classes are initialized only once: when they are constructed.
The meaning is similar to that of the copy constructor. When the parameter type is not const, the assignment from the const object generates an error. The reverse is not true: if you assign const to a value other than const, the assignment succeeds.

move constructor and move assignment operator
The following example of
is based on the C++ class MemoryBlock used to manage memory buffers.


// MemoryBlock.h
#pragma once
#include <iostream>
#include <algorithm>

class MemoryBlock
{
public:

 // Simple constructor that initializes the resource.
 explicit MemoryBlock(size_t length)
  : _length(length)
  , _data(new int[length])
 {
  std::cout << "In MemoryBlock(size_t). length = "
    << _length << "." << std::endl;
 }

 // Destructor.
 ~MemoryBlock()
 {
  std::cout << "In ~MemoryBlock(). length = "
    << _length << ".";

  if (_data != nullptr)
  {
   std::cout << " Deleting resource.";
   // Delete the resource.
   delete[] _data;
  }

  std::cout << std::endl;
 }

 // Copy constructor.
 MemoryBlock(const MemoryBlock& other)
  : _length(other._length)
  , _data(new int[other._length])
 {
  std::cout << "In MemoryBlock(const MemoryBlock&). length = " 
    << other._length << ". Copying resource." << std::endl;

  std::copy(other._data, other._data + _length, _data);
 }

 // Copy assignment operator.
 MemoryBlock& operator=(const MemoryBlock& other)
 {
  std::cout << "In operator=(const MemoryBlock&). length = " 
    << other._length << ". Copying resource." << std::endl;

  if (this != &other)
  {
   // Free the existing resource.
   delete[] _data;

   _length = other._length;
   _data = new int[_length];
   std::copy(other._data, other._data + _length, _data);
  }
  return *this;
 }

 // Retrieves the length of the data resource.
 size_t Length() const
 {
  return _length;
 }

private:
 size_t _length; // The length of the resource.
 int* _data; // The resource.
};

The following procedure shows how to write a move constructor and a move assignment operator for the example C++ class.
creates a move constructor for C++
defines an empty constructor method that takes an rvalue reference to a class type as an argument, as shown in the following example:


MemoryBlock(MemoryBlock&& other)
 : _data(nullptr)
 , _length(0)
{
}

In the move constructor, add the class data member from the source object to the object to be constructed:


_data = other._data;
_length = other._length;

Assigns the data members of the source object to default values. This prevents the destructor from releasing resources (such as memory) multiple times:


other._data = nullptr;
other._length = 0;

creates a move assignment operator for the C++ class
defines an empty assignment operator that takes an rvalue reference to a class type as an argument and returns a reference to a class type, as shown in the following example:


MemoryBlock& operator=(MemoryBlock&& other)
{
}

In the move assignment operator, if you try to assign an object to itself, add a conditional statement that does not perform the operation.


if (this != &other)
{
}

In a conditional statement, all resources (such as memory) are freed from the object to which they are assigned.
The following example frees the _data member from the object to which it is assigned:


// Free the existing resource.
delete[] _data;

Perform steps 2 and 3 of procedure 1 to transfer the data members from the source object to the object to be constructed:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;
0

Returns a reference to the current object, as shown in the following example:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;
1

The following example shows the full move constructor and move assignment operator for the MemoryBlock class:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;
2

The following example demonstrates how mobile semantics can improve application performance. This example adds two elements to a vector object, and then inserts a new element between two existing elements. In Visual C++ 2010, the vector class USES move semantics to perform inserts efficiently by moving vector elements instead of copying them.


// rvalue-references-move-semantics.cpp
// compile with: /EHsc
#include "MemoryBlock.h"
#include <vector>

using namespace std;

int main()
{
 // Create a vector object and add a few elements to it.
 vector<MemoryBlock> v;
 v.push_back(MemoryBlock(25));
 v.push_back(MemoryBlock(75));

 // Insert a new element into the second position of the vector.
 v.insert(v.begin() + 1, MemoryBlock(50));
}

The example produces the following output:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;
4

This version of the sample that USES mobile semantics is more efficient than the version that does not because it performs fewer replication, memory allocation, and memory release operations.
reliable programming
to prevent resource leaks, always free resources (such as memory, file handles, and sockets) in the move assignment operator.
To prevent unrecoverable resource corruption, properly handle self-assignments in the move assignment operator.
If you provide both a move constructor and a move assignment operator for your class, you can write a move constructor to call the move assignment operator, thereby eliminating redundant code. The following example shows a modified version of the mobile constructor that invokes the move assignment operator:


TextFile a, b;
a.Open( "FILE1.DAT" );
b.Open( "FILE2.DAT" );
b = a;
5


Related articles: