C++ primer Chapter 13 review
13.1 copy, assignment and destruction
Class has five special member functions that control object copy, movement, assignment and destruction
- copy constructor
- copy assignment operator
- move constructor
- Move assignment operator
- Destructor
The above operations are called copy control operations. If a class does not define these functions, the compiler will automatically generate the missing functions
copy constructor
Copy constructors should not normally be explicit (explicit cannot implicitly initialize objects)
class Foo{ public: Foo(){} //Constructor Foo(const Foo&){} //copy constructor };
In C + +, if the constructor has only one parameter, there will be a default conversion at compile time: convert the data type corresponding to the constructor to this kind of object, such as
CxString string2 = 10 //Convert to CxString string2(10); or CxString temp(10); CxString string2 = temp;
#include <iostream> using namespace std; class Test{ public: Test(){} //copy constructor Test(const Test &t){ cout << "in copy" << endl; data = t.data; } Test& Test::operator=(const Test &t){ std::cout << "in =" << std::endl; return *this; } private: int data; }; int main(){ Test t1; Test t2(t1);//Because the copy constructor is explicitly called, it has been compiled Test t3; t3 = t2; Test t4 = t2;//Since the copy constructor is implicitly called, it cannot be compiled return 0; } //The = operator function () cannot be called without an initialized object, so if you want to use the = operator function, you need Test t4; t4 = t2; Two steps Therefore, Test t4 = t2 will be optimized to Test t4(t2) by the compiler
Composite copy constructor
When copying other classes is involved, the copy constructor will call the copy construction of other classes
class Sales_data{ public: Sales_data(const Sales_data&); private: std::string bookNo; int units_sold = 0; }; Sales_data::Sales_data(const Sales_data& orig): bookNo(orig.bookNo), //Call the copy constructor of string units_sold(orig.units_sold){}
copy initialization
The copy constructor is used to initialize non reference type parameters, so its own parameters must be references (if they are not references, the copy constructor of parameters will be called)
std::string dots(10, '.'); //Constructor Initializers std::string str1(dots); //Explicit copy constructor initialization std::string str2 = dots; //Implicit copy constructor initialization std::string str3 = "9-99-999"; //Constructor Initializers std::string str4 = std::string(100, '9');//Explicit constructor + implicit copy constructor initialization
Restrictions on copy initialization
explicit single parameter constructor cannot be copied and constructed with = member value. The same is true for function parameters
explicit vector(size_type n); //Constructor for vec vector (const vector& x); //Copy constructor for vec std::vector<int> v1(10); //Correct, constructor initialization std::vector<int> v2 = 10; //Error, constructor is explicit void f(std::vector<int>); f(10); //Error, constructor is explicit f(std::vector<int>(10)); //Correct, STD:: vector < int > TMP = STD:: vector < int > (10);
Compile bypass copy constructor
std::string str3 = "9-99-999"; //Rewrite as std::string str3("9-99-999"); //Direct constructor initialization instead of implicit constructor + implicit copy constructor initialization
copy assignment operator
The copy assignment operator accepts a parameter of the same type as the calling class
class Foo{ public: Foo(){} Foo(const Foo&){} //The copy assignment operator accepts a parameter of the same type as the calling class Foo& operator==(const Foo&){} }; int main(){ Foo f2; Foo f1; f1 = f2; }
Composite copy assignment operator
If the copy operator of other classes is involved, the copy operator will call the copy operator of other classes
class Sales_data{ public: Sales_data(const Sales_data&); Sales_data& operator=(const Sales_data&); private: std::string bookNo; int units_sold = 0; }; Sales_data& Sales_data::operator=(const Sales_data&){ bookNo = orig.bookNo; units_sold = orig.units_sold; //Call the copy operator of string return *this; }
Destructor
Parameters are not accepted and overloading is not allowed
Constructor initializes the non static data member of the object (initializes the member first, then constructor parent - > child)
Destructor releases object members and destroys non static data members (call destructor first, and then release member child - > parent)
First call A destructor, then destroy A data member, and finally destroy parent class B, which is just the opposite of creation
class Foo{ public: ~Foo(){} };
1. Member initialization order: only related to the declaration order of class members
2. The difference between initialization list and constructor body initialization: (except for built-in data types)
Initialization in the member initialization list and assignment in the constructor body, built-in data types and composite types (pointers and references) have exactly the same performance and results, but the results of user-defined types (class types) are the same, but there are differences in performance. Since the class type data member object has been constructed before entering the function body, the object construction work is carried out in the member initialization list. If the assignment in the constructor body is equivalent to carrying out the constructor first and then calling the copy operator, while the member initialization list only calls the copy constructor function
Synthetic destructor
After the destructor of the class ends, the destructor of the member will be called automatically
class Foo{ public: //Members are automatically destroyed without additional action ~Foo(){} private: std::string data; };
Classes that require destructors also require copy and assignment operations
Destruct the class that releases the pointer, which requires copy function and assignment operation; The class that needs copy operation also needs assignment operation, and vice versa
Assign a unique ID to each object of a class: you need a custom copy constructor and assignment operator, but you don't need a custom destructor
class HashPtr{ public: //Constructor HashPtr(const std::string& s = std::string()) : data(new std::string(s)), num(0){} //Compiler generated copy function HashPtr(const HashPtr& hashPtr){ (*this).data = hashPtr.data; this->num = hashPtr.num; } //Compiler generated copy operator const HashPtr& operator=(const HashPtr& hashPtr){ (*this).data = hashPtr.data; this->num = hashPtr.num; return *this; } //Destructor ~HashPtr(){ delete data; } private: std::string* data; int num; }; HashPtr test(HashPtr hashPtr){ //Call the copy generated by the compiler to construct HashPtr hashPtr(p1) HashPtr ret = hashPtr; //Call the compiler generated copy operator return ret; // The hashPtr object and ret will be destroyed } int main(){ HashPtr p1("some values"); test(p1); //After the call, P1 The memory pointed to by data has been released HashPtr p2(p1); //Now the data of P1 and P2 becomes a wild pointer system("pause"); return 0; }
Explicit default function
(= default). Default can only be used for 6 special member functions, but delete can be used for any member function
=default can explicitly require the compiler to generate a composite version
class Sales_data{ public: Sales_data() = default; Sales_data(const Sales_data&) = default; Sales_data& operator=(const Sales_data&); ~Sales_data() = default; private: std::string m_data; }; // =The object type returned by the assignment operator function generated by default is sales_ Data & non const Sales_data& Sales_data::operator=(const Sales_data&) = default;
Block copy
For example, the IOStream class prevents copying to avoid writing or reading the same IO cache to multiple objects
struct NoCopy { NoCopy() = default; //Informs the compiler that you do not want to define these member functions NoCopy(const NoCopy&) = delete; //Block copy NoCopy& operator=(const NoCopy&) = delete; ~NoCopy() = default; }; struct NoDtor { NoDtor() = default;//Default construction ~NoDtor() = delete;//Prevent deconstruction };
For destructing a deleted class, you cannot define the object of the class or release the class pointer
struct NoDtor { NoDtor() = default;//Default construction ~NoDtor() = delete;//Prevent deconstruction }; int main(){ //NoDtor nd; // An error is reported and the compilation is detected because the object cannot be destroyed normally NoDtor* p = new NoDtor(); delete p; //report errors system("pause"); return 0; }
In essence, when class members cannot be copied, assigned or deleted, the synthetic copy function of the class has been defined as deleted
Before C++11 1, copy blocking was implemented through private, but friend and member functions can still be copied
struct PrivateCopy { private: PrivateCopy(const PrivateCopy&); PrivateCopy& operator=(const PrivateCopy&); public: //The default composition constructor can be used, and class objects cannot be copied PrivateCopy() = default; ~PrivateCopy() = delete; };
Singleton mode
Implementation = delete implementation singleton
template<typename T> class Singleton { public: static T& GetInstance() { static T instance; return instance; } Singleton(T&&) = delete; Singleton(const T&) = delete; void operator= (const T&) = delete; protected: Singleton() = default; virtual ~Singleton() = default; };