C++ copy constructor

Article directory

  • Preface
  • 1. Concept
  • 2.Characteristics
  • 3.Default copy constructor
  • 4. Deep copy, shallow copy
    • a.Shallow copy
    • b. Deep copy
  • Summarize

Foreword

In the last article, I briefly introduced the two special functions of the class-Constructor and destructor. The constructor is mainly used to initialize the member variables of the object, and the analysis function The constructor is mainly used to clean up the battlefield after the battle. When we do not write these functions, the compiler will automatically generate default constructors and destructors to help us run the program reasonably. However, in some cases, the ones generated by the compiler cannot meet our needs for code, which requires We write it ourselves, so we have to write selectively according to different situations.

Next, I will continue to introduce another special member function of the class-Copy Constructor

1. Concept

Copy constructor: There is only a single formal parameter. This formal parameter is a reference to this class type object (generally commonly used const modification). When creating a new object with an existing class type object, the compiler automatically transfer.
When copying a custom type object, the copy constructor will be called

2. Features

The copy constructor is an overloaded form of the constructor
There is only one parameter to the copy constructor and it must be a reference to a class type object. If you use the pass-by-value method, the compiler will directly report an error because it will cause infinite recursive calls.

class Date
{<!-- -->
public:
Date(int year = 1900, int month = 1, int day = 1)
{<!-- -->
_year = year;
_month = month;
_day = day;
}
Date(const Date & amp; d) // Wrong writing: Compilation error will cause infinite recursion
{<!-- -->
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{<!-- -->
Date d1;
Date d2(d1);
return 0;
}

Why does using value-passing cause infinite recursive calls? ?

Draw a picture to simulate the compiler’s operation using value transfer:

As shown in the figure above, execute date d2(d1); d1 passes the parameter d to the formal parameter d of the copy constructor. That is, when you need to call the copy constructor of class A, you need to pass in an object of A as an actual parameter by value. So now The object only has d1, so date d(d1) will appear, and its own copy constructor will be called during the copying process. The value passing method will continue to pass in an object of A as an actual parameter, and the recursion will continue endlessly. .

3.Default copy constructor

? ?When we do not write a copy constructor in the class, the compiler will automatically generate a default copy constructor.

? ?The copy constructor generated by the system will also make a distinction between the built-in type and the custom type of the member variable. For built-in type member variables, the compiler will complete the copy according to the memory storage byte order of the copied object. For example, if the copied object has 3 int type member variables, occupying 12 bytes of memory, the compiler will complete the copy according to the memory storage byte order of the object. Memory and member initial values are copied to the new object.

class Time
{<!-- -->
public:
Time()
{<!-- -->
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time & t)
{<!-- -->
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time & amp;)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{<!-- -->
private:
//Basic type (built-in type)
int _year = 1970;
int _month = 1;
int _day = 1;
// Custom type
Time_t;
};
int main()
{<!-- -->
Date d1;
Date d2(d1);
return 0;
}

When creating d2, the default copy constructor of the class is called. The compiler copies the same d1 data value for all members of the built-in types: _year, _month, _day
For the custom type member _t, it will jump to the Time class and call the copy constructor of the Time class.

In the default copy constructor generated by the compiler, the built-in type is copied directly in byte format , and the custom type calls its copy constructor to complete the copy.

4. Deep copy, shallow copy

a.Shallow copy

For the date code in the above example, you don’t need to write a copy constructor and just use the default one. However, in special cases, there will be different results:

typedef int DataType;
class Stack{<!-- -->
public:
    Stack(size_t capacity = 10){<!-- -->
        _array = (DataType*)malloc(capacity * sizeof(DataType));
        if (nullptr == _array){<!-- -->
            perror("malloc failed to apply for space");
            return;
        }
        _size = 0;
        _capacity = capacity;
    }
    void Push(const DataType & amp; data){<!-- -->
        // CheckCapacity();
        _array[_size] = data;
        _size + + ;
    }
    ~Stack(){<!-- -->
        if(_array)
        {<!-- -->
            free(_array);
            _array = nullptr;
            _capacity = 0;
            _size = 0;
        }
    }
private:
    DataType* _array;
    size_t _size;
    size_t _capacity;
};
int main(){<!-- -->
    Stack s1;
     s1.Push(1);
    s1.Push(2);
    s1.Push(3);
    s1.Push(4);
    Stack s2(s1);
    return 0;
}


We will find that the member variables _array of s1 and s2 both point to the same space. That is to say, when the compiler calls the default structure generated by the system, it also copies the address pointed to by s1._array to s2._array. This does not seem to matter. Waiting until the end of the program is complete, calling the destructor can cause big problems!

The s1 object is created by calling the constructor. In the constructor, a space of 10 elements is applied for by default and 4 elements are stored in it 1 2 3 4
The s2 object uses the s1 copy constructor, and the Stack class does not explicitly define a copy constructor. The compiler will generate a default copy constructor for the Stack class. The default copy constructor copies the content in s1 based on the value. Copy it intact to s2. Therefore s1 and s2 point to the same memory space.
When the program exits, s2 and s1 must be destroyed. S2 is destroyed first. When s2 is destroyed, the destructor is called. The space of 0x11223344 has been released, but s1 does not know that when s1 is destroyed, the space of 0x11223344 will be released again, a piece of memory. Freeing space multiple times will definitely cause the program to crash.
The previous date classes were all ordinary built-in type member variables, so there was no need to write them specially. The compiler could complete the copy independently. This kind of copy is called shallow copy.

b. Deep copy

//deep copy
    Stack(const Stack & amp; st) {<!-- -->
        _array = (DataType*)malloc(sizeof(DataType) * st._capacity);
        if (_array == nullptr) {<!-- -->
            perror("malloc fail");
            return;
        }
        //You only need to create a heap space of the same size as st1, and the rest is to copy the data of st1
        memcpy(_array, st._array, _capacity = st._capacity);
        _size = st._size;
        _capacity = st._capacity;
    }


Deep copy means that the default structure generated by the compiler alone cannot meet the needs, and you need to write one independently, which is called deep copy.

Classes that need to write destructors need to write copy constructors (Stack class)

There is no need to write a destructor for the class, the copy constructor generated by default can be used. (Date class)

Summary

The above is our detailed introduction to the relevant content of the C++ copy constructor. I hope it will be helpful to everyone’s study. It is for reference only. If there are any mistakes, please tell me and I will correct them as soon as possible. Welcome everyone to comment~~~