C++ basics-classes and objects (Part 1)

Preface

In C++ language, we use classes to define our own data types. By defining new types to reflect various concepts in the problem to be solved, we can make it easier to write, debug and modify programs . Today we will have a preliminary understanding of classes and learn some class-related knowledge.

1. Object-oriented and process-oriented

1. Overview of Object Oriented (OOP)

The core ideas of object-oriented programming are data abstraction, inheritance and dynamic binding. By using data abstraction we can separate the interface and implementation of a class; using inheritance we can define similar types and model similar relationships; using dynamic binding we can ignore the differences between similar types to a certain extent and use Use their objects in a unified way.
We will not cover class inheritance and dynamic binding earlier, these will be unlocked slowly after we have a certain foundation. We will study the abstraction of data, that is, our encapsulation accordingly.

2. The difference between object-oriented and process-oriented

The C language we use is process-oriented, focusing on the process, analyzing the steps to solve the problem, and gradually solving the problem through function calls.
Insert image description here
C++ is based on object-oriented and focuses on objects . It splits one thing into different objects and relies on the interaction between objects.
Insert image description here
In C++, we only need to put the clothes and corresponding things into the washing machine, and the washing machine will give us the washed clothes. We do not need to care about how the washing machine washes the clothes and spins them dry.

2. Introduction and definition of classes

1. Introduction of classes

The structure in C language can only define variables, but in C++, not only variables but also functions can be defined in the structure. For example, when we previously implemented a linked list in C language, we could only define variables in the structure; but in C++, we can also put the corresponding functions into the struct structure.

struct STU
{
    
    
	void Init(char* name, int math, int chinese)//初始化函数
	{
    
    
		memcpy(_name, name, sizeof(char) * 10);
		_math = math;
		_chinese = chinese;
	}
	void Total_score()//计算总分
	{
    
    
		_total_score = _math + _chinese;
	}
	void print()//打印相关信息
	{
    
    
		cout << "姓名:" << _name << "  数学:" << _math << "  语文:" << _chinese << "  总分:" << _total_score << endl;
	}
	char _name[10];//学生姓名
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _total_score;//学生总成绩
};
int main()
{
    
    
	struct STU stu;
	char name[10] = "zhangsan";
	stu.Init(name, 80, 80);//调用我们结构体中的初始化函数
	stu.Total_score();//调用我们结构体中的计算总分函数
	stu.print();//调用我们结构体中的打印函数
	return 0;
}

Insert image description here
In the definition of the structure above, it is preferred to use class (class keyword) instead of struct in C++. We will see the difference between struct and class below.

2. Basic ideas and definitions of classes

1. The idea of ​​class

The basic idea of ​​classes is data abstraction and encapsulation . Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation. The interface of a class includes the operations that users can perform: the implementation of the class includes the data members of the class, the function body responsible for the interface implementation, and various private functions required to define the class. Encapsulation realizes the separation of interface and implementation of a class. The encapsulated class hides its implementation details, which means that users of the class can only use the interface and cannot access the implementation part.

2. Encapsulation concept

1. Concept:

Organically combine data and methods of operating data, hide the properties and implementation details of objects, and only expose interfaces to interact with objects.

2. Function:

Encapsulation is essentially a kind of management that makes it easier for users to use classes. In C++, data and data manipulation methods can be organically combined through classes, and access permissions can be used to hide the internal implementation details of objects and control which methods can be used directly outside the class. This allows us to hide our data.

1. Class definition

class className //className是类的名字,和我们上面结构体中的STU一样
{
    
    
	// 类体:由成员函数和成员变量组成
};//和结构体一样,后面有分号

class is the keyword that defines the class, ClassName is the name of the class, {} is the main body of the class, and the semicolon at the end of the class definition cannot be omitted like the structure.
The contents in the class body are called members of the class: variables in the class are called attributes or member variables of the class; functions in the class are called methods or member functions of the class.

class STU
{
    
    
public:
	void Show()//成员函数
	{
    
    
		cout << "姓名:" << _name << "  数学:" << _math << "  语文:" << _chinese << endl;
	}
private:
	//成员变量
	char _name[10];//学生姓名
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	void Init(char* name, int math, int chinese);//成员函数
};

Two ways to define classes:

  1. All declarations and definitions are placed in the class body. If member functions are defined in the class, the compiler may treat them as inline functions.
  2. The declaration and definition of the class are separated, and the class name needs to be added before the member function name::
    Insert image description here
    We try to use the second type in implementation.

3. Characteristics of classes

1. Access qualifiers and scopes

Careful readers may have discovered pbulic and private in the class. What are these? What's the function?

1. Class access qualifiers

The way C++ implements encapsulation: Use classes to combine the properties and methods of objects to make the objects more complete, and selectively provide their interfaces to external users through access rights. The above is our access rights.
Insert image description here
Public: Publicly modified members can be directly accessed outside the class
. Protected: They cannot be accessed outside the class, but can be accessed within their own member functions.
private: similar to protection, the specific differences will be expanded later.
Note: The access scope begins at the occurrence of this access qualifier and ends at the occurrence of the next access qualifier.
Now let us solve the above question, the difference between struct and class in C++: The default access permission of class is private, while the default access permission of struct is public (because in C++, struct must be compatible with C)

2. Scope of class

The class defines a new scope.

class STU
{
    
    
public:
	void Show();//成员函数
private:
	//成员变量
	char _name[10];//学生姓名
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
};
//这里需要指定Show函数是属于STU这个类域
void STU::Show()//成员函数
{
    
    
	cout << "姓名:" << _name << "  数学:" << _math << "  语文:" << _chinese << endl;
}

The fact that a class is a scope explains why we must provide the class name and function name when we define member functions outside the class. Outside the class, the member names are hidden. Once the class name is encountered, the rest of the definition is within the scope of the class. The rest of the definition here includes the parameter list and function body, so that we can directly use other members of the class without authorization again.

2. Class instantiation and size

1. Instantiation of a class

The process of creating an object from a class type is called instantiation of the class.
A class describes an object. It is something like a model. It limits the members of the class. Defining a class does not allocate actual memory space to store it, but the instantiated object occupies actual physical space and stores it. Class member variables, for example: instantiating objects from a class is like using architectural design drawings to build a house in reality, and classes are like design drawings.

int main()
{
    
    
	STU._math = 100;//编译失败:STU类是没有空间的,只有STU类实例化出的对象才有具体的成绩
	struct STU stu;//类的实例化
	return 0;
}

2. Class size

A class can have both member variables and member functions. So what does an object of a class contain? How to calculate the size of a class?

class STU
{
    
    
public:
	void Show();//成员函数
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
};
int main()
{
    
    
	cout << sizeof(STU) << endl;
	return 0;
}

Insert image description here
We can see that the result of sizeof calculation is only the size of our member variables.
So what is the size in the code below?

class STU
{
    
    
public:
	void Show();//成员函数
private:
	//成员变量
	static int age;
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
};
int main()
{
    
    
	cout << sizeof(STU) << endl;
	return 0;
}

Insert image description here
We see that the result after the member variable contains a static variable is still 8. Why is this?
In the classes we create: the size of a class is actually the sum of the "member variables" in the class. Of course, we must pay attention to memory alignment and the size of empty classes. Empty classes are special. The compiler gives empty classes one byte. An object that uniquely identifies this class. Our member functions and static variables can be used by all instantiations, and they belong to the public part of the class.

3.The introduction of this

class STU
{
    
    
public:
	void Init(int math, int chinese, int english)
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	void Total_score()//计算总分
	{
    
    
		_total_score = _math + _chinese + _english;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << "  总分:" << _total_score << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	int _total_score;//学生总成绩
};
int main()
{
    
    
	STU stu1;
	STU stu2;
	stu1.Init(90, 85, 100);
	stu2.Init(100, 100, 100);
	stu1.Total_score();
	stu2.Total_score();
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
In the above conclusion, we know that the member functions of the class are public, so how does the compiler know that we are the function called by stu1 and the function called by stu2?
C++ solves this problem by introducing the this pointer. The C++ compiler adds a hidden pointer parameter to each "non-static member function" so that the pointer points to the current object (the object that calls the function when the function is running). In the function All operations on "member variables" in the body are accessed through this pointer. It's just that all operations are transparent to the user, that is, the user does not need to pass it, the compiler automatically completes it.
For example we call:

stu1.Total_score();

The compiler is responsible for passing the address of stu1 to the implicit parameter this of Total_score. It can be equivalently considered that the compiler rewrites the call into the following form:

//用于说明调用成员函数的实际执行过程,并不是真正的代码
STU::Total_score(&stu1);

Insert image description here
Through debugging, we can see that this pointer records the address of our caller. This pointer is generated by the compiler by default and does not require us to explicitly pass parameters. Any behavior of customizing parameters or variables named this is illegal.
Characteristics of this pointer:

  1. The type of this pointer: class type * const, that is, in member functions, this pointer cannot be assigned a value, so we are not allowed to change the address saved in this.
  2. Can only be used inside "member functions".
  3. The this pointer is the first implicit pointer parameter of the "member function". Generally, it is automatically passed by the compiler through the ecx register and does not need to be passed by the user.

4. Class type

Each class defines a unique type. For two classes, even if their members are exactly the same, the two classes are two different types.

class stu
{
    
    
	void Init();
	int _math;
};
class STU
{
    
    
	void Init();
	int _math;
};
int main()
{
    
    
	stu stu1;
	STU stu2 = stu1;//错误,stu1和stu2的类型不同
}

Insert image description here
Even if the member lists of two classes are exactly the same, they are different types.

5.Friends of classes

In the above code, our students' grades are private and cannot be accessed outside the class. So how can we access them outside the class? The method is to make other classes or functions its friends.
For example:

class STU
{
    
    
public:
	friend void outInit(STU& stu, int math, int chinese);
	void Init(int math, int chinese)
	{
    
    
		_math = math;
		_chinese = chinese;
	}

	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
};
void outInit(STU& stu, int math, int chinese)
{
    
    
	stu._math = math;
	stu._chinese = chinese;
}
int main()  
{
    
    
	STU stu1;
	STU stu2;
	stu1.Init(100, 100);//类成员函数调用
	outInit(stu2, 90, 90);//普通函数调用
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
When we use ordinary functions, we need to pass parameters to our class, because ordinary functions do not have our this pointer, and initialization within the class does not require us to pass parameters to the class. When we declare ordinary member functions as friends of a class, our ordinary functions can access our private members.
Insert image description here

4. Default member functions and characteristics of classes

A class has 6 default member functions. When the user does not implement them explicitly, the compiler will generate these member functions.
Insert image description here
Let's get to know these default member functions formally.

1.Default construction

1. Constructor concept

The constructor is a special member function with the same name as the class. It is automatically called by the compiler when creating a class type object to ensure that each data member has an appropriate initial value and is only called once during the entire life cycle of the object. .

class STU
{
    
    
public:
	STU(int math, int chinese = 0, int english = 0)//构造函数
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()
{
    
    
	STU stu1(90, 85, 100);
	STU stu2(80);
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
Insert image description here
From the above, we can see that our constructor can also be a default function. Our constructor is automatically called when we instantiate the class.

2. Characteristics of constructor

The constructor is a special member function. The main task of the constructor is not to open space to create an object, but to initialize the object.
The characteristics of the constructor are as follows:

  1. The function name is the same as the class name.
  2. No return value.
  3. The compiler automatically calls the corresponding constructor when the object is instantiated.
  4. Constructors can be overloaded.
  5. If there is no explicitly defined constructor in the class, the C++ compiler will automatically generate a parameterless default constructor. Once the user explicitly defines it, the compiler will no longer generate it.
  6. Both parameterless constructors and fully default constructors are called default constructors, and there can only be one default constructor. Note: No-argument constructors, all-default constructors, and constructors generated by default by the compiler that we have not written can all be considered default constructors.

In the following code, for convenience, we set the member variables of the custom type as public.

class STU
{
    
    
public:
	STU()//构造函数
	{
    
    }
	STU(int math, int chinese = 0, int english = 0)//构造函数
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()  
{
    
    
	STU stu1;
	STU stu2(90, 85, 100);
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
Observing the above results, we can see that when we call stu1, we call the constructor without parameters, so the data of stu1 are all random values, while stu2 calls the overloaded function, and the value in stu2 is the one we assigned. .
Notice:

STU stu1;//对STU进行实例化得到stu1
STU stu2();//不可以实例化,编译器会认为一个函数的声明

Our second calling method is wrong! ! !

3.Default constructor

From the characteristics of the constructor, we can see that when we do not explicitly declare a constructor, the compiler will generate one for us. What is the generated result?

class PEO
{
    
    
public:
	int _age;//年龄
	int _height;//身高
};
class STU
{
    
    
public:
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
		cout << "年龄:" << peo._age << "  身高:" << peo._height << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	PEO peo;//自定以类型
};
int main()  
{
    
    
	STU stu1;
	stu1.Show();
	return 0;
}

Insert image description here
Our STU does not provide an explicit constructor, and PEO does not provide an explicit constructor. The results we print out are all random values.

class PEO
{
    
    
public:
	PEO()
	{
    
    
		_age = 20;
		_height = 180;
	}
	int _age;//年龄
	int _height;//身高
};
class STU
{
    
    
public:
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
		cout << "年龄:" << peo._age << "  身高:" << peo._height << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	PEO peo;//自定以类型
};
int main()
{
    
    
	STU stu1;
	stu1.Show();
	return 0;
}

Insert image description here
Our STU does not provide an explicit constructor, while PEO provides an explicit constructor. The built-in type in the printed result of STU is a random value, and the custom type calls its own constructor.

class PEO
{
    
    
public:
	PEO()
	{
    
    
		cout << "PEO()" << endl;
		_age = 20;
		_height = 180;
	}
	int _age;//年龄
	int _height;//身高
};
class STU
{
    
    
public:
	STU()
	{
    
    
		cout << "STU()" << endl;
		_math = 100;
		_chinese = 100;
		_english = 100;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
		cout << "年龄:" << peo._age << "  身高:" << peo._height << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	PEO peo;//自定以类型
};
int main()
{
    
    
	STU stu1;
	stu1.Show();
	return 0;
}

Insert image description here
When we have defined constructors, the corresponding constructor will be called when we instantiate it, and we can also see it by printing the results.
Insert image description here
From the above cases and corresponding results, we can find that: in C++, the default constructor does not process built-in types (such as int, char, etc.), and calls the custom type's constructor for custom types. This is also why we see random values.
Note: In C++11, a patch has been made for the defect that built-in type members are not initialized, that is, built-in type member variables can be given default values ​​when declared in a class.

class STU
{
    
    
public:
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math = 0;//学生数学成绩
	int _chinese = 0;//学生语文成绩
	int _english = 0;//学生英语成绩
};
int main()
{
    
    
	STU stu1;
	stu1.Show();
	return 0;
}

Insert image description here
NOTE: Available in C++11! ! !
Let’s compare the results printed by the function below:
Insert image description here
Why are we different from the above? We do not provide corresponding constructors for built-in types. Why are the values ​​of our built-in types also initialized?
Let's take a look at the running results under Linux:
Insert image description here
Comparison found that: the custom type in our class contains pointers, and there is no explicit constructor in the custom type. When there is an explicit constructor of the built-in type in the class, vs will Types are also initialized, but not under Linux. This is a compiler problem! ! !

2.Default destruction

1. Concept of destructor

Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself. The local object destruction is completed by the compiler. When the object is destroyed, the destructor will be automatically called to complete the cleanup of resources in the object.

class STU
{
    
    
public:
	STU()
	{
    
    
		cout << "构造函数:STU()" << endl;
	}
	~STU()
	{
    
    
		cout << "析构函数:~STU()" << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()
{
    
    
	STU stu1;
	return 0;
}

Insert image description here
We can see that the destructor is called by default like our constructor.
We can release the requested space in the destructor, which can effectively avoid our memory leaks.

2. Destructor characteristics

The characteristics of the destructor are as follows:

  1. The destructor name is preceded by the character ~ before the class name.
  2. No parameters and no return value type.
  3. A class can only have one destructor. If not explicitly defined, the system will automatically generate a default destructor. Note: Destructors cannot be overloaded
  4. When the object life cycle ends, the C++ compilation system automatically calls the destructor.

3.Default destructor

class PEO
{
    
    
public:
	~PEO()
	{
    
    
		cout << "析构函数:~PEO()" << endl;
	}
	int _age;//年龄
	int _height;//身高
	int* _tmp;
};
class STU
{
    
    
public:
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	PEO peo;
};
int main()
{
    
    
	STU stu1;
	return 0;
}

Insert image description here
We do not directly create an object of the PEO class in the main method at all. Why is the destructor of the PEO class called in the end?
Reason: The STU object stu1 is created in the main method, and d contains 4 member variables, of which __math, _chinese, and _english are built-in type members. They do not require resource cleanup when destroyed. Finally, the system can directly recycle its memory. ; and peo is a PEO class object, so when stu1 is destroyed, the peo object of the PEO class contained inside it must be destroyed, so the destructor of the PEO class must be called.
However: the destructor of the PEO class cannot be directly called in the main function. What is actually to be released is the STU class object, so the compiler will call the destructor of the STU class. If STU is not explicitly provided, the compiler will give the STU class Generate a default destructor with the purpose of calling the destructor of the PEO class internally. That is, when the STU object is destroyed, it is necessary to ensure that each custom object inside it can be destroyed correctly. The PEO class is not directly called in the main function. destructor, instead explicitly calling the compiler-generated default destructor for STU classes.
Note: The destructor of which class is called when an object of that class is created, and the destructor of that class is called when an object of that class is destroyed.
To sum up, the destructor is the same as the constructor. It does not process the built-in types and calls the corresponding destructor for the custom types.

3.Copy construction

1.Copy constructor concept

Copy constructor: There is only a single formal parameter. This formal parameter is a reference to an object of this class type (generally commonly used with const modification). It is automatically called by the compiler when creating a new object with an existing class type object.

class STU
{
    
    
public:
	STU(int math = 0, int chinese = 0, int english = 0)
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	STU(STU& stu)//拷贝构造函数
	{
    
    
		_math = stu._math;
		_chinese = stu._chinese;
		_english = stu._english;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()
{
    
    
	STU stu1(100,100,100);
	STU stu2(stu1);
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here

2. Copy constructor characteristics

The characteristics of copy construction are as follows:

  1. The copy constructor is an overloaded form of the constructor.
  2. 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.
  3. If not explicitly defined, the compiler will generate a default copy constructor. The default copy constructor object copies the object in byte order according to memory storage. This type of copy is called shallow copy, or value copy.
    Insert image description here
    We must use references. If we don’t use references, the compilation will not pass. Let’s use a piece of code to see why we need to pass references:
class STU
{
    
    
public:
	STU()//构造函数
	{
    
    }
	STU(STU& stu)//拷贝构造函数
	{
    
    
		cout << "拷贝构造函数:STU()" << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
void Fun(STU stu)
{
    
    
	cout << "普通函数:Fun()" << endl;
}
int main()
{
    
    
	STU stu1;
	Fun(stu1);
	return 0;
}

Insert image description here
When we call a normal function, because it is a call by value, our actual parameters will be copied and the copy constructor will be called. If our copy constructor is still a call by value, the copy constructor will call the copy constructor and keep recursing. So our copy constructor needs to use references.

class STU
{
    
    
public:
	STU()//构造函数
	{
    
    
		tmp = (int*)malloc(sizeof(int));
	}
	STU(STU& stu)//拷贝构造函数
	{
    
    
		cout << "拷贝构造函数:STU()" << endl;
	}
	~STU()//构造函数
	{
    
    
		free(tmp);
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
	int* tmp;
};
int main()
{
    
    
	STU stu1;
	STU stu2(stu1);
	return 0;
}

Insert image description here

A shallow copy is performed at this time. Let's take a look at the shallow copy through debugging:
Insert image description here
We can see that the tmp address of stu1 and the tmp address of stu2 are the same, proving that these two addresses point to the same space. When we release the space, the same space will be to release. This causes the program to crash. The solution to the problem is to perform a deep copy.
We usually use our copy constructor when the function return value type is a class type object, use an existing object to create a new object, or the function parameter type is a class type object.

4. Assignment overloading

1. Overloading concept

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name.
The function name is: the keyword operator is followed by the operator symbol that needs to be overloaded.
Function prototype: Return value type operator operator (parameter list)

Note:
New operators cannot be created by connecting other symbols: such as operator@.
Overloaded operators must have a class type parameter.
Operators used for built-in types cannot change their meaning. For example, the built-in integer type + cannot change its meaning.
When overloaded as a class member function, its formal parameters appear to be 1 less than the number of operands, because the first parameter of the member function is hidden this.
.* :: sizeof ?: . These five operators cannot be overloaded.

We will focus on overloading in the next chapter. Today we will look at our assignment overloading.

2. Concept of assignment and overloading

Assignment operator overloading format:
Parameter type: const T&, passing reference can improve the efficiency of parameter passing.
Return value type: T&, returning reference can improve the efficiency of return. The purpose of having return value is to support continuous assignment. To detect
whether you assign a value to yourself,
return *this : To compound the meaning of continuous assignment

class STU
{
    
    
public:
	STU(int math = 0, int chinese = 0, int english = 0)
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	STU& operator=(const STU& stu)
	{
    
    
		if (this != &stu)
		{
    
    
			_math = stu._math;
			_chinese = stu._chinese;
			_english = stu._english;
		}
		return *this;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()
{
    
    
	STU stu1(100,100,100);
	STU stu2; 
	stu2 = stu1;
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
The assignment operator can only be overloaded as a member function of the class and cannot be overloaded as a global function.
Reason: If the assignment operator is not implemented explicitly, the compiler will generate a default one. At this time, if the user implements a global assignment operator overload outside the class, it will conflict with the default assignment operator overload generated by the compiler in the class. Therefore, the assignment operator overload can only be a member function of the class.
When we don't implement it explicitly, the compiler will generate a default assignment operator overload that copies byte by value by value.

class STU
{
    
    
public:
	STU(int math = 0, int chinese = 0, int english = 0)
	{
    
    
		_math = math;
		_chinese = chinese;
		_english = english;
	}
	void Show()
	{
    
    
		cout << "数学:" << _math << "  语文:" << _chinese << "  英语:" << _english << endl;
	}
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};
int main()
{
    
    
	STU stu1(100,100,100);
	STU stu2; 
	stu2 = stu1;
	stu1.Show();
	stu2.Show();
	return 0;
}

Insert image description here
Similarly, we cannot directly use the default assignment overload where space needs to be opened, otherwise the two pointers will still point to the same space.

5. Rule of Three/Five

When we need a destructor we also need copy and assignment operations.
Reason: The built-in type system will automatically collect it. We need to release the memory we applied for in the destructor. If we use the default copy and assignment, then the shallow copy behavior will be performed, and the program will crash when we destruct.
If a class needs a custom destructor, it almost certainly also needs a custom copy assignment operator and copy constructor.
If a class requires a copy constructor, it almost certainly also requires a copy assignment operator. Vice versa, when our class requires a copy assignment operator, it must require a copy constructor. However, neither the need for a copy constructor nor the copy assignment operator necessarily implies the need for a destructor.

6. Prevent copying

In some cases where we don't want the class to generate these functions by default, we can define a delete function. Indicate the function we wish to define for deletion by appending =delete.

class STU
{
    
    
public:
	STU() = default;//使用默认的构造函数
	STU(STU&stu) = delete;//阻止拷贝
	~STU() = default;//使用默认的析构函数
private:
	//成员变量
	int _math;//学生数学成绩
	int _chinese;//学生语文成绩
	int _english;//学生英语成绩
};

The destructor cannot be a deleted member.

Summarize

We have already started with the class, and we will study the class further in the next article.

Guess you like

Origin blog.csdn.net/2301_76986069/article/details/132891626