C++ primer Chapter 13 review 13.1

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

  1. copy constructor
  2. copy assignment operator
  3. move constructor
  4. Move assignment operator
  5. 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;
};

Keywords: C++ Back-end

Added by lttldude9 on Sun, 23 Jan 2022 14:30:11 +0200