copy construction
class Stu{ public: int no; string name; int age; public: Stu(int no=10086, string name="jin", int age = 18):no(no), name(name), age(age){} //Copy constructor: construct a new copy object from objects of the same type Stu(const Stu &s){ no = s.no; name = s.name; agr = s.age } }; int main() { Stu s1 = (10010, "jink", 100); //Copy the constructor, but the constructor Stu is not called, but the copy constructor Stu s2(s1); }
definition
Construct a new copy object from an existing object of the same type, and call the copy constructor
-
The form of the copy constructor is fixed:
class Class name{ Class name( const Class name& object){ } };
essence
Copy constructor is also a constructor, which is a special
- The special parameter is an object reference of the same type
If you do not add a copy constructor to a class when implementing it, the compiler will provide a default
The default copy constructor is: full copy (byte by byte copy). The copy is the same as the meta object
Programmers can construct copy constructors according to their own requirements. After construction, the compiler will no longer provide default copy constructors
Timing of use
- Non reference transfer object
- The objects put into the container are all through the copy constructor
- Construct new objects of the same type with existing objects
Call syntax
- Class name object 1 = object 2;
- Class name new object (old object);
When to use non default copy constructors
- If you need to implement deep copy, you need to implement the copy constructor by yourself. The default copy constructor is shallow copy
- When there is a pointer to dynamic memory, it is generally necessary to implement the copy constructor
- Shallow copy - copy by byte copy by byte copy the copied object is exactly the same as the original object
- For pointers, only memory addresses are copied
- Deep copy - for ordinary data, there is no deep copy, which refers to the content of the copy pointer
- For the pointer, reapply the memory and copy the content pointed to by the original pointer into the new memory space
Therefore, when a pointer points to dynamic memory, it is generally necessary to use a deep copy of independent construction
class Ptr{ private: int *ptr; public: Ptr(const Ptr &pp){} }
Copy assignment function
Time point of copy assignment
When two existing objects of the same type are assigned to each other, copy assignment is called
The copy assignment function is as follows:
class Class name{ Class name& operator(const Class name &Object name){} }
If a class does not implement the copy assignment function, the compiler provides the default copy assignment function, which is a shallow copy
If you need to implement deep copy, you need to provide it manually by the programmer
Generally speaking: follow the three / five principle
- Before C++11, there were three: destruction, copy construction, copy assignment. Either all of them are provided by themselves, or all of them are used by default
- C++11 is followed by five: destruction, copy construction, copy assignment, mobile construction, mobile assignment
Implement a simple string class
class String{ public: //Construct String objects with C-style strings String(const char *s = NULL): str(strcpy(new char[s?strlen(s)+1:1, s?s:"")){} ~String(void){ if(str != nullptr){ delete[] s; str = nullptr; } } //The copy constructor uses the copy of objects of the same type to construct a copy object of the same type String (const String &s): str(strcpy(new char[strlen(s.str)+1, s.str){} String &operator=(const String& ss){ if(this != s){ String _tmp(s); swap(str,_tmp.str); //Can't see the free memory, but it has the free delete []_ tmp.str //_ tmp object life cycle expires after the end of the statement block, and destructs are automatically called } return *this; } size_t length(void)const{ return strlen(str); } const String *c_str(void)const{ return str; } prevate: char *str; };
Encapsulate a stack and simple tests:
#include <bits/stdc++.h> using namespace std; class Stack{ public: Stack(int cap, int sizes = 0): cap(cap){ elems = new int[cap]; } //elems(new int[cap]) ~Stack(void){ if(sizes != 0){ delete[] elems; elems = nullptr; } } Stack(Stack &stack){ cap = stack.capacity(); sizes = stack.size(); int *Elem = new int[stack.cap]; for(int i = 0; i < stack.size(); i++){ Elem[i] = stack.elems[i]; } } Stack &operator = (Stack &stack){ if(this != &stack){ Stack _tmp(stack); swap(elems, _tmp.elems); cap = stack.capacity(); sizes = stack.size(); } return *this; } void push(int elem){ if(full()){ throw out_of_range("full");//Throw an exception <stdecept> } elems[sizes++] = elem; } int pop(void){ if(empty()){ throw out_of_range("empty"); } return elems[--sizes]; } bool full(){ return cap == sizes; } bool empty(){ return sizes == 0; } int top(void){ if(empty()){ throw out_of_range("empty"); } return elems[sizes-1]; } int size(void){ return sizes; } int capacity(void){ return cap; } private: int cap; int sizes; int *elems; }; int main() { Stack stack(10); for(int i = 0; i < 10; i++){ stack.push(i); } for(int i = 0; i < 10; i++){ cout<<stack.pop()<<endl; } }
Default nonparametric construction
- Default nonparametric Construction: when a class does not implement a constructor, the compiler automatically provides it,
- If you need to explicitly call the parameterized constructor, you need to provide the member name and arguments
- Default copy constructor: the copy constructor of class type members will be called in the initialization list
- Need to copy members
- Default copy assignment function: the copy assignment function of the member of the class type will be called in the function body
- Need to copy members
- Default destructor: the destructor of the member of the class type will be called automatically
There are at least the following member functions for an empty class
- Nonparametric structure
- copy construction
- copy assignment
- Destructor
- &Value function t* operator& (void)
- &Constant address function const t* operator& (void) const
- If only a copy constructor is provided in a class, it has only one member function of the copy constructor
Mobile structure
class Class name{ public: Class name(Class name&& obj){ } };
-
Use an existing object to construct a new object of the same type, but the old object will die soon. For the sake of efficiency, there is no need to call copy construction (causing a waste of resources, copy one, destruct one). At this time, you can choose to move the structure, and move (give) the resources of the old object to the new object. It is necessary to ensure that the old object can be destructed normally
-
Old objects can no longer be used
Move assignment
- Assign a value to another existing object with an existing object. Under normal circumstances, the assigned resource should be destructed, and then copy the resource of another object to the assigned object. However, if = the object on the right will die soon, for the sake of efficiency, it is not necessary to copy a copy to the assigned object and directly hand over (transfer) the resource to the assigned object
- =The object on the right can no longer be used
class Class name{ public: Class name& operator=(Class name&& obj){ } };
Singleton mode
Mode: the summarized development routine can reduce code repetition, improve code reliability and efficiency, and realize specific functions
Singleton mode:
Definition: a class can only create one object
- Lazy mode
- Don't move unless you have to. Only when you are hungry can you find something to eat
- Objects in singleton mode are created only when there is a requirement
class SingleTon{ public: static SingleTon* getInstance(void){ if(ps == nullptr){ ps = new SingleTon(); } return ps; } void realse(void){ --cnt; if(cnt == 0){ delete ps; ps = nullptr; } } ~SingleTon(){ } private: int no; string name; private: SingleTon(){} SingleTon(const SingleTon& s){} static SingleTon *ps;//Pointer to unique instance static int cnt; }; SingleTon SingleTon::ps = nullptr; int SingleTon::cnt = 0; int main() { SingleTon *p1 = SingleTon::getIntance(); }
Advantages and disadvantages
- It can be created only when there is a need, which saves memory space and can be released in time when it is used up
- Thread is unsafe. Two threads may create multiple instances at the same time, so thread synchronization needs to be considered, and the efficiency of thread synchronization becomes low
- Hungry man mode
- I'm hungry and need to be ready to eat
- The single instance mode object of this mode always exists, whether there is a demand or not
class SingleTon{ public: static SingleTon* getInstance(void){ return s; } ~SingleTon(){} private: int no; string name; static SingleTon s; private: SingleTon(){} SingleTon(const SingleTon& s){} static SingleTon s;//Unique instance }; SingleTon SingleTon::s;
Advantages and disadvantages
- No matter whether there is demand or not, it always occupies memory and wastes memory space
- Hungry man mode is to instance in the program loading stage, which can ensure that there is only one thread and thread safety