The basis of C++ study notes

foreword

  This study note is a summary of personal study. Some relatively simple and common knowledge points are not included. It is mainly to supplement and explain the doubts in the learning process. This note is only for reference and study, not suitable for tutorials.

1. Fragmentary knowledge points

Constants: #defineand const, constants cannot be modified when the program is running
1. Are macro constants strings?
Answer: Its function is to define an identifier and a string with the #define compiler preprocessing directive. When the identifier is found in the source program, it will be replaced with the specified string.
2. Identifiers are case-sensitive, and it is best to know the meaning by name.

insert image description here
By default, decimals are treated as double precision, and f is added to single precision. When outputting decimals by default, there are 6 significant digits (both single and double precision).
Scientific notation: 1e-3, 1e-6
String:
C style:
insert image description here

C++ style:
insert image description here
ifThere are three types of structures
switchthat need to be paid attention to when using them, breakthe effects of adding and not adding, and defaultthe effects of adding or not.
Random number generation:

int num = rand()% 100 + 1: // rand()%100 +1生成 0+1 99 +1 随机数

random seed

//添加随机数种子 作用利用当前系统时间生成随机数,防止每次随机数都一样
srand((unsigned int)time (NULL)):

insert image description here
switchIt can only be integer or character type, because integer and character type can actually be understood as a data type, but the number of bytes occupied is different.
Jump statement breakstatement:

Function: It is used to jump out of the selection structure or breakthe timing of the loop structure:

  • Appears in the switch conditional statement, the function is to terminate the case and jump out of the switch
  • Appears in a loop statement, the function is to jump out of the current loop statement
  • Appears in a nested loop, jumping out 最近of the inner loop statement

Note the mixed use of break in loops and selections, break only works on statements related to itself.

goto //跳转到某一句

It is not recommended to use, breaking the top-down operation structure of C++

goto FLAG;//直接跳转到下面的FLAG:后面的语句
cout << "3"<< endl;
cout << "4" << endl;
FLAG:
cout << "4" << endl;
system("pause") ;

Array:
Three ways to define a dimensional array:

1. Data type array name [array length];
2. Data type array name [array length] = {value1, value2...};
3. Data type array name[] = {value1, value2...};

Array features: each element in the array is placed in a continuous memory space of the same data type.
The length of the array cannot be determined by variables.
The purpose of the one-dimensional array name

1. Can count the length of the entire array in memory
2. Can get the first address of the array in memory

The array name is a constant
bubble sort:

1. Compare adjacent elements. If the first is bigger than the second, swap them both.
2. Do the same work for each pair of adjacent elements, and after execution, find the first maximum value.
3. Repeat the above steps, each time the number of comparisons -1, until no comparison is required

insert image description here
Two-dimensional array: the number of columns cannot be omitted

1. Data type array name [row number] [column number];
2. Data type array name [row number] [column number]={ { {data 1, data 2, data 3}, {data 4, data 5, data 6}}
3. Data type array name[rows][columns]={data 1, data 2, data 3, data 4, data 5, data 6} 4. data type array
name[][columns]= {Data 1, Data 2, Data 3, Data 4, Data 5, Data 6} The compiler can automatically capture the number of lines

Two-dimensional array name:

1. You can view the size of the occupied memory space.
2. You can view the first address of the two-dimensional array

insert image description here
Function:
The definition of a function generally has 5 steps

1. Return value type
2. Function name
3. Parameter list
4. Function body statement
5. Return expression

Passing by value:
formal parameter changes will not affect actual parameters and
voidreturn values ​​cannot be added

Four functional forms:

1. No participation, no return
2. Participation, no return
3. No participation, return
4. Participation, return

Function declaration:
Tell the compiler that the function exists in advance, the compiler must see it in advance, the declaration can be multiple times, but the definition can only be once.

Function file writing:
function file writing generally has 4 steps:

1. Create .ha header file with the suffix name 2. Create a source file with the
suffix name 3. Write the declaration of the function in the header file. Generally, the library to be used in the source file should also be written here, for example , 4. In Write the definition of the function in the source file.cpp
iostreamusing namespace std;

  It is not enough to just do the above steps. You also need to include a custom header file in the source file, which is to include the price name of the header file you created. The purpose of this step is to include the custom header file Associated with the source file, some declarations required in the source file are also written in the header file instead of in the source file. When using it, you only need to add the header file of the file you need to use at the beginning of the place you use.

insert image description here
insert image description here
insert image description here
If the main program wants to call the function defined just now, it can be used directly by directly importing the header file.

insert image description here
Pointer:
generally occupies four bytes, takes up the same space on each compiler, and has nothing to do with the data type.
Null pointers and wild pointers

Null pointer: The pointer variable points to the space with memory number 0
Purpose: Initialize the pointer variable
Note: The memory pointed to by the null pointer cannot be accessed, and the memory number between 0-255 is occupied by the system, so it cannot be accessed

//空指针
//1、空指针用于给指针变量进行初始化
int *p = NULL;
//2、空指针是不可以进行访问的,企图让p指向的内存填上数字100,是不可以的
*p = 100;

The null pointer cannot be accessed, because the number 0-255 in memory is occupied by the system.
Wild pointer:
pointer variable points to illegal memory space

int main(
//野指针,随便让他们指向一个内存编号
//在程序中,尽量避免出现野指针
int *p = (int *)0x1100;
//访问野指针报错
cout << *p << end1;
system("pause");
return 0;

For this part, you can refer to the following: C++ wild pointer summary

constDecorating the pointer
constcan ensure that the value will not be changed when the pointer is passed.
constThere are three cases for modifying pointers:

1. const modified pointer - constant pointer
2. const modified constant - pointer constant
3. const not only modified pointer, but also modified constant

See who is behind the const?
Constant pointer:

const int * p = &a;

The pointing of the pointer can be changed, but the value pointed to by the pointer cannot be changed
as a pointer constant:

int * const p = &a;

The pointing of the pointer cannot be changed, but the value pointed to can be changed
const means modifying the pointer and modifying the constant:

const int * const p = &a;

Neither the pointing of the pointer nor the value pointed to by the pointer can be changed.
Memory method:
  translate constdirectly into a constant, and "*"then translate it into a pointer to clearly distinguish between a pointer constant and a constant pointer const. Whether it is a pointer or a finger "*", whoever follows it cannot be changed or manipulated.
insert image description here

Tips: See constif the one on the right is a pointer or a constant, if it is a pointer, it is a constant pointer, if it is a constant, it is a pointer constant

Structure:
Syntax:

struct 结构体名 {
    
    结构体成员列表 };

There are three ways to create variables through structures:

  1. struct structure name variable star name.
  2. struct structure name variable name = {member 1 value, member 2 value...}.
  3. Variables are created by the way when defining structures.

Note: In C++, it can be omitted when creating a structure variable struct, but it cannot be omitted in C.
Summarize:

1: The keyword when defining a structure is struct, which cannot be omitted
2: When creating a structure, the keyword struct can be omitted
3: The structure variable uses the operator " ." to access members

Structure pointer:

Use the operator -> to access the structure properties through the structure pointer

There are two sentences in the above code that I forgot to explain:

system("pause")//按任意键退出系统
system("cls")//清屏

The ternary operation can be placed in the output statement:
insert image description here

switch case:

If the case is followed by a compound statement, it must be enclosed in curly braces.

Array deletion ideas:

Find the label to be deleted, and the following data will move forward as a whole.

Two. C++ core

2.1. Memory partition

Memory partition model:
here is very important, very important, very important!

When the C++ program is executed, the general direction of memory is divided into 4regions

  • Code area: store the binary code of the function body, managed by the operating system.
  • Global area: store global variables and static variables and constants
  • Stack area: automatically allocated and released by the compiler, storing function parameter values , local variables , etc.
  • Heap area: allocated and released by the programmer, if the programmer does not release it, it will be reclaimed by the operating system at the end of the program

The meaning of the four memory areas:

The data stored in different areas are endowed with different life cycles, giving us more flexible programming

Before the program runs: After the program is compiled, an executable program
is generated . Before the program is executed, it is divided into two areas: the code area and the global area code area:exe

Store the machine instructions executed by the CPU.
The code area is shared . The purpose of sharing is that for frequently executed programs, only one copy of the code is required in the memory. The code
area is read-only . The reason for making it read-only is Prevents a program from accidentally modifying its instructions

Global zone:

​ Global variables and static variables are stored here.
​ The global area also contains the constant area, string constants and other constants are also stored here
.The data in this area is released by the operating system after the program ends.

Constant:
string constant
constdecoration, divided into global and local

insert image description here
insert image description here
Summary: C++ is divided into global area and code area
before the program runs

The code area is characterized by sharing and read-only.
Global variables, static variables, and constants are stored
in the global area. constModified global constants and string constants are stored in the constant area. Local variables modified by const are not placed in the global area. Macro constants are not allocated to the heap or global area, they are just replaced with their defined constant values ​​during the preprocessing phase. Therefore, macro constants only exist in the code, and will not be allocated memory space when the program is running. The following points should be noted.

After the program runs:
stack area:

Automatically allocated and released by the compiler, storing function parameter values, local variables, etc.

Note: Do not return the address of the local variable, the data created in the stack area will be automatically released by the compiler , as shown in the following example:

int* func()
{
    
    
	int a = 10;//局部变量 存放在栈区,栈区的数据在函数执行完后自动释放
	return &a; //返回局部变量的地址
}
int main() {
    
    
	//接受func函数的返回值
	int *p = func();
	cout << *p << endl; //第一次可以打印正确的数字,是因为编译器做了一次保留,只做一次保留; 
	cout << *p << endl; //第二次这个数据就不再保留了,此时相当于野指针
	system(pause");
	return 0;
}

Heap area:

It is allocated and released by the programmer. If the programmer does not release it, it will be recovered by the operating system at the end of the program.
In C++, it is mainly used to newopen up memory in the heap area

int *func()
{
    
    
	//利用new关键字 可以将数据开辟到堆区
	//指针本质上也是局部变量,放在栈上,指针保存的数据是放在堆区
	int *p = new int(10); //返回的也是地址
	return p;
}
int main() {
    
    
	//在堆区开辟数据
	int *p = func();
	cout << *p << endl; 
	//只要不手动释放,在程序退出前可以一直输出
	cout << *p << endl; 
	cout << *p << endl; 
	cout << *p << endl; 
	system(pause");
	return 0;
}

new operator:

In C++, the new operator is used to develop data in the heap area. The data developed in the heap area is manually developed by the programmer and released manually. The release uses the operator syntax: , the returned data is the data created by the pointer using new, and the data corresponding to the data delete
will new 数据类型be
returned type of pointer

void test010()
{
    
    
	int * p = func();
	cout << *p <<endl;
	cout << *p <<endl;
	cout << *p <<endl;
	//堆区的数据 由程序员管理开辟,程序员管理释放
	//如果想释放堆区的数据,利用关键字 delete
	delete p; //释放p指向的内容
	//报错,读取访问权限冲突,此时再访问就是野指针
	cout << *p <<endl;
}

Create an array in the heap:

void test02(){
    
    
	//创建10整型数据的数组,在堆区
	int * arr = new int[10]: //10代表数组有10个元素,返回数组的首地址
	}

//释放堆区数组
//释放数组的时候 要加[]才可以
delete[] arr;

2.2. References

Reference
Aliases are given to variables. In the same storage space, if one changes, the other also changes.
To quote the basic syntax:

data type & alias = original name

Notice:

  • reference mustinitialization, you have to tell the compiler whose alias it is when you come up
  • After the reference is initialized, it cannot be changed.Can only be an alias for a variable, from beginning to end
int a = 10;
//1、引用必须初始化//int &b;  错误,必须要初始化
int &b = a;
//2、引用在初始化后,不可以改变
int c = 20;
//赋值操作,而不是更改引用,相当于把c的值赋给了a和b,a和b完全等价,最终a,b,c三个值都是20.
b = c;

Quoted as arguments to functions:

Function: When passing parameters in a function, the reference technique can be used to let the formal parameter modify the actual parameter.
Advantages: It can simplify the pointer to modify the actual parameter

Notice:

When using a reference as a function parameter, the formal parameter and the actual parameter share the same memory space. That is to say, the value of the actual parameter is directly passed to the formal parameter, and the modification of the formal parameter in the function will be directly reflected on the actual parameter.

Summarize:

Passing parameters by reference has the same effect as passing by address. The syntax for citations is clearer and simpler.

Reference as the return value of a function:
A reference can exist as a return value of a function.
Notice:

1. Do not return local variable references, as mentioned aboveDo not return the address of a local variableThere is a reason.
2. Function calls as lvalues

//返回局部变量引用,会引起意想不到的错误,相当于野指针 
int& test01() {
    
    
	int a = 10; //局部变量
	return a;
}

//返回静态变量引用
int& test02() {
    
    
	static int a = 20; //静态变量,存放在全局区,程序运行完才释放
	return a;
}

int main() {
    
    

	//不能返回局部变量的引用。这里说明一下,函数返回的是引用,接受的时候可以用引用也可以不用引用,
	//取决于是否需要对返回值进行修改。
	int& ref = test01();
	//第一次正确,编译器做了一次保留
	cout << "ref = " << ref << endl;
	//第二次错误,因为a的内存已释放
	cout << "ref = " << ref << endl;

	//如果函数做左值,那么必须返回引用
	//上面声明的是静态的,这里可以无限次使用
	int& ref2 = test02();
	//下面两句都能正常输出
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;
	
	//如果函数的返回值是引用,这个函数的调用可以作为左值
	test02() = 1000;

	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;

	system("pause");

	return 0;
}

Explain the above code again:
insert image description here

The nature of the quote:

The essence of the reference is implemented inside C++ as apointer constant

C++It is recommended to use reference technology , because the syntax is convenient, and the essence of reference is a pointer constant, but all pointer operations are done for us by the compiler

//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
    
    
	ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
    
    
	int a = 10;
    
    //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
	int& ref = a; 
	ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
    
	cout << "a:" << a << endl;
	cout << "ref:" << ref << endl;
    
	func(a);
	return 0;
}

In the above example, ref is internally found to be a reference, and it is automatically converted to the format of: *ref = 20

Constant reference:
modify the formal parameters to prevent misuse, and use the modification as much as C++possible in the standard writing . The reference must refer to a legal memory space:constconst

int a = 10:
int & ref = 10;//引用必须引一块合法的内存空间(栈区或者堆区的数据),这里会报错,因为10是放在常量区的
//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
    
    
	//v += 10;
	cout << v << endl;
}

int main() {
    
    

	//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
	/*加入const就可以了,编译器将代码修改为:int temp = 10; const int& ref = temp;
	创建了一个临时变量就可以引用了,
	其实这里引用的是个中间变量,我们找不到他的原名,原名是编译器起的,我们只能通过别名操作。*/
	const int& ref = 10; //加入const之后变为只读,不可修改

	//ref = 100;  //加入const后不可以修改变量
	cout << ref << endl;

	//函数中利用常量引用防止误操作修改实参
	int a = 10;
	showValue(a);

	system("pause");

	return 0;
}

Constant references are usually written in formal parameters

2.3. Functions

The default parameters of the function:

If the data is passed in, use your own, if not, use the default.
If there is already a default parameter at a certain position, then there must be default parameters starting from this position, that is, the default parameter must be placed behind.
If the declaration of the function has default parameters, then the implementation of the function cannot have default parameters, or only one of the declaration and the implementation can have default parameters.

Placeholder parameters of functions:
Syntax: 返回值类型 函数名 (数据类型){}
  When passing parameters, the placeholders must also fill in the parameters. Placeholder parameters can also have default parameters, which will be used later. When there are default parameters, there is no need to pass parameters.

//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int) {
    
    
	cout << "this is func" << endl;
}

int main() {
    
    

	func(10,10); //占位参数必须填补,否者报错

	system("pause");

	return 0;
}

Function overloading:
function names can be the same.
Overload condition : three conditions are met at the same time, the core is that the parameters must be different

  • Under the same scope, that is, the function you want to call comes from the same place
  • same function name
  • function parametersdifferent typesorThe number is differentordifferent order

Note: The return value
of a function cannot be used as a condition for overloading.

//函数重载需要函数都在同一个作用域下
void func()
{
    
    
	cout << "func 的调用!" << endl;
}
void func(int a)
{
    
    
	cout << "func (int a) 的调用!" << endl;
}
void func(double a)
{
    
    
	cout << "func (double a)的调用!" << endl;
}
void func(int a ,double b)
{
    
    
	cout << "func (int a ,double b) 的调用!" << endl;
}
void func(double a ,int b)
{
    
    
	cout << "func (double a ,int b)的调用!" << endl;
}

//函数返回值不可以作为函数重载条件
//int func(double a, int b)
//{
    
    
//	cout << "func (double a ,int b)的调用!" << endl;
//}


int main() {
    
    

	func();
	func(10);
	func(3.14);
	func(10,3.14);
	func(3.14 , 10);
	
	system("pause");

	return 0;
}

Note on function overloading:

  • References can be used as overload conditions
  • Function overloading encounters function default parameters

Example:

//函数重载注意事项
//1、引用作为重载条件
void func(int &a)
{
    
    
	cout << "func (int &a) 调用 " << endl;
}

void func(const int &a)
{
    
    
	cout << "func (const int &a) 调用 " << endl;
}


//2、函数重载碰到函数默认参数

void func2(int a, int b = 10)
{
    
    
	cout << "func2(int a, int b = 10) 调用" << endl;
}

void func2(int a)
{
    
    
	cout << "func2(int a) 调用" << endl;
}

int main() {
    
    
	
	int a = 10;
	func(a); //调用无const
	func(10);//调用有const,并且int& a = 10也不合法,但是const int& a = 10,相当于创建了一个临时的变量


	//func2(10); //碰到默认参数产生歧义,不知道调用哪个了,需要避免这种情况。有函数重载的时候尽量不要使用默认参数。这里传两个参数可以。

	system("pause");

	return 0;
}

The core of function overloading: there can be no ambiguity when calling a function.

2.4. Classes and Objects

C++The most important content in the object-oriented:encapsulationinheritpolymorphism. Let's start with encapsulation.
Package:

Encapsulation is C++one of the three major characteristics of object-oriented
. The meaning of encapsulation:

  • Attributes and behaviors as a whole, representing things in life
  • Control attributes and behaviors with permissions

Package meaning:

​ When designing a class, attributes and behaviors are written together to represent things

grammar: class 类名{ 访问权限: 属性 / 行为 };

Look at an example of a simple class: design a circle class, find the circumference of the circle

//圆周率
const double PI = 3.14;

//1、封装的意义
//将属性和行为作为一个整体,用来表现生活中的事物

//封装一个圆类,求圆的周长
//class代表设计一个类,后面跟着的是类名
class Circle
{
    
    
public:  //访问权限  公共的权限

	//属性
	int m_r;//半径

	//行为
	//获取到圆的周长
	double calculateZC()
	{
    
    
		//2 * pi  * r
		//获取圆的周长
		return  2 * PI * m_r;
	}
};

int main() {
    
    
	//通过圆类,创建圆的对象
	// c1就是一个具体的圆
	Circle c1;
	c1.m_r = 10; //给圆对象的半径 进行赋值操作

	//2 * pi * 10 = = 62.8
	cout << "圆的周长为: " << c1.calculateZC() << endl;

	system("pause");

	return 0;
}

Design a student class, the attributes include name and student number, you can assign values ​​to the name and student number, and you can display the student's name and student number.

//学生类
class Student {
    
    
public:
	void setName(string name) {
    
    
		m_name = name;
	}
	void setID(int id) {
    
    
		m_id = id;
	}

	void showStudent() {
    
    
		cout << "name:" << m_name << " ID:" << m_id << endl;
	}
public:
	string m_name;
	int m_id;
};

int main() {
    
    

	Student stu;
	stu.setName("德玛西亚");
	stu.setID(250);
	stu.showStudent();

	system("pause");

	return 0;
}

Access to classes and objects: including functions and variables

  1. public public permission members can be accessed within the class and can be accessed outside the class, generally used for external interfaces
  2. protected protected permission member class can access the protected content in the parent class when the child cannot access the inheritance
  3. private Private permission member class can be accessed outside the class and cannot be accessed. When inheriting, the son cannot access the private content in the parent class

Let's see an example:

//三种权限
//公共权限  public     类内可以访问  类外可以访问
//保护权限  protected  类内可以访问  类外不可以访问
//私有权限  private    类内可以访问  类外不可以访问

class Person
{
    
    
	//姓名  公共权限
public:
	string m_Name;

	//汽车  保护权限
protected:
	string m_Car;

	//银行卡密码  私有权限
private:
	int m_Password;

public:
	void func()
	{
    
    
		m_Name = "张三";
		m_Car = "拖拉机";
		m_Password = 123456;
	}
};

int main() {
    
    

	Person p;
	p.m_Name = "李四";
	//p.m_Car = "奔驰";  //保护权限类外访问不到
	//p.m_Password = 123; //私有权限类外访问不到

	system("pause");

	return 0;
}


structThe difference between protected permissions and private permissions that cannot be accessed outside the class class:
From the definition of the class, it can be seen that the definition of struct and class is very similar.

In C++ structand classthe only difference is thatThe default access rights are different

the difference:

  • structThe default permission is public, if the member does not add permission modifier, the default ispublic
  • classThe default permission is private, if the member does not add permission modifier, the default isprivate
class C1
{
    
    
	int  m_A; //默认是私有权限
};

struct C2
{
    
    
	int m_A;  //默认是公共权限
};

int main() {
    
    

	C1 c1;
	c1.m_A = 10; //错误,访问权限是私有

	C2 c2;
	c2.m_A = 10; //正确,访问权限是公共

	system("pause");

	return 0;
}

It can also be seen from the above example that
setting member properties as private: this is generally done in projects, and the advantage of classes is privacy.

Advantage 1: Set all member attributes as private, and you can control the read and write permissions yourself
Advantage 2: For write permissions, we can check the validity of the data

class Person {
    
    
public:

	//姓名设置可读可写
	void setName(string name) {
    
    
		m_Name = name;
	}
	string getName()
	{
    
    
		return m_Name;
	}

	//设置年龄
	void setAge(int age) {
    
    
		if (age < 0 || age > 150) {
    
    
			cout << "请输入正确的年龄!" << endl;
			return;
		}
		m_Age = age;
	}

	//获取年龄 
	int getAge() {
    
    
		return m_Age;
	}
	
	//爱人设置为只写
	void setLover(string lover) {
    
    
		m_Lover = lover;
	}

private:
	string m_Name; //可读可写  姓名
	int m_Age; //只读  年龄
	string m_Lover; //只写  爱人
};


int main() {
    
    

	Person p;
	//姓名设置
	p.setName("张三");
	cout << "姓名: " << p.getName() << endl;

	//年龄设置
	p.setAge(50);
	cout << "年龄: " << p.getAge() << endl;

	//爱人设置
	p.setLover("xh");
	//cout << "爱人: " << p.m_Lover << endl;  //只写属性,不可以读取

	system("pause");

	return 0;
}

A class can have another class as a member, see the example below.

#include<iostream>
using namespace std;

//点和圆的关系
// 点类
class Point
{
    
    
public:
	//设置x坐标
	void setX(int x)
	{
    
    
		m_X = x;
	}
	//获取x坐标
	int getX()
	{
    
    
		return m_X;
	}
	//设置Y坐标
	void setY(int y)
	{
    
    
		m_Y = y;
	}
	//获取Y坐标
	int getY()
	{
    
    
		return m_Y;
	}

private:
	int m_X;
	int m_Y;

};

//圆类
class Circle
{
    
    
private:
	int m_R;
	Point m_Center;

public:
	//设置半径
	void setR(int r)
	{
    
    
		m_R = r;
	}
	//获取半径
	int getR()
	{
    
    
		return m_R;
	}
	//设置圆心
	void setCenter(Point center)
	{
    
    
		m_Center = center;
	}
	//获取圆心
	Point getCenter()
	{
    
    
		return m_Center;
	}
};

//判断点和圆的关系
void isInCircle(Circle& c, Point& p)
{
    
    
	//计算两点之间距离平方
	int distance =
		(c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
		(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
	//计算半径平方
	int rDistance = c.getR() * c.getR();
	//判断关系
	if (distance == rDistance)
	{
    
    
		cout << "点在圆上" << endl;
	}
	else if (distance > rDistance)
	{
    
    
		cout << "点在圆外" << endl;
	}
	else
	{
    
    
		cout << "点在圆内" << endl;
	}
}

int main() {
    
    
	//创建圆
	Circle c;
	c.setR(10);
	Point center;
	center.setX(10);
	center.setY(10);
	c.setCenter(center);
	//创建点
	Point p;
	p.setX(10);
	p.setY(10);
	//判断关系
	isInCircle(c, p);

	system("pause");
	return 0;
}

  Does the above code look messy, without any level, which is very unfavorable for engineering development. Let's sort it out by file type.
insert image description here
insert image description here
insert image description here
insert image description here
After finishing the above, does the code look much more concise?

2.4.1. Object initialization and cleanup

2.4.2. Constructor and destructor

Object initialization and cleanup are also two very important security issues

​ An object or variable has no initial state, and the consequences of its use are unknown.
Similarly, after using an object or variable, if it is not cleaned up in time, it will also cause certain security problems

C++ uses constructors and destructors to solve the above problems. These two functions will be automatically called by the compiler to complete object initialization and cleanup.
The initialization and cleanup of objects is what the compiler forces us to do, so if we don't provide construction and destruction, the compiler will provide

Constructors and destructors provided by the compiler are empty implementations.

  • Constructor: The main function is to assign values ​​to the member properties of the object when creating the object. The constructor is determined by the compilerautomatic call, no need to call manually.
  • Destructor: the main role is in the objectbefore destructionThe system calls automatically to perform some cleaning work.

Constructor syntax:类名(){}

  1. Constructor, no return value and no writevoid
  2. The function name is the same as the class name
  3. Constructors can have parameters, so overloading can occur
  4. When the program calls the object, it willautomatic callConstruction, no need to call manually, and it will only be called once

Destructor syntax: ~类名(){}

  1. Destructor, no return value and no writevoid
  2. The function name is the same as the class name, prefix the name with a symbol~
  3. Destructors cannot have parameters, so overloading cannot occur
  4. The program will automatically call the destructor before the object is destroyed, no need to call it manually, and it will only be called once

Core: one is responsible for initialization and one is responsible for destruction.

class Person
{
    
    
public:
	//构造函数
	Person()
	{
    
    
		//如果自己不写,系统默认为空,这里什么都不写
		cout << "Person的构造函数调用" << endl;
	}
	//析构函数
	~Person()
	{
    
    
	//如果自己不写,系统默认为空,这里什么都不写
		cout << "Person的析构函数调用" << endl;
	}

};
//构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构
void test01()
{
    
    
	Person p; //在栈上的数据,test01执行完毕后释放对象。如果把创建对象放到main函数中,要等程序运行完才释放,即对象销毁后才释放
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

2.4.3. Classification and call of constructor

Two classification methods:

  1. ​According to the parameters, it is divided into: construction with parameters and construction without parameters (default) construction​
  2. Divided by type: ordinary construction and copy construction

Three calling methods:

  1. ​Brackets​
  2. display method
  3. implicit conversion method
//1、构造函数分类
// 按照参数分类分为 有参和无参构造   无参又称为默认构造函数
// 按照类型分类分为 普通构造和拷贝构造

class Person {
    
    
public:
	//无参(默认)构造函数
	Person() {
    
    
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int a) {
    
    
		age = a;
		cout << "有参构造函数!" << endl;
	}
	//拷贝构造函数,拷贝构造顾名思义拷贝一份类对象,但是又不能改变本体,拷贝的同时按照引用的方式传值
	Person(const Person& p) {
    
    
		//将传入的人身上的所有属性,拷贝到我身上
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}
	//析构函数
	~Person() {
    
    
		cout << "析构函数!" << endl;
	}
public:
	int age;
};

//2、构造函数的调用
//调用无参构造函数
void test01() {
    
    
	Person p; //调用无参构造函数
}

//调用有参的构造函数
void test02() {
    
    

	//2.1  括号法,常用,推荐使用
	Person p1; //默认构造函数
	Person p11(10);//有参构造函数
	Person p12(p1);//拷贝构造函数,传对象
	//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明,不认为是创建对象
	//Person p2();

	//2.2 显式法
	Person p2; //默认构造函数
	Person p21 = Person(10); //有参构造函数,可以理解成Person p21(10)
	Person p22 = Person(p2); //拷贝构造函数,可以理解成Person p22(p2)
	//Person(10)单独写就是匿名对象,当前行结束之后,马上析构,即这个对象调用完,下面的函数还没执行就已经销毁了
	//不要用拷贝构造函数初始化匿名对象:Person(p2),会提示Person p2重定义,因为此时编译器会认为 Person p2;上面已经有一个了

	//2.3 隐式转换法
	Person p4 = 10; // Person p4 = Person(10); 有参构造函数
	Person p5 = p4; // Person p5 = Person(p4); 拷贝构造函数

	//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
	//Person (p4);
}

int main() {
    
    
	test01();
	//test02();
	system("pause");
	return 0;
}

2.4.4. When to call the copy constructor

Three times:

  • Initialize a new object using an already created object
  • The method of value passing is to pass values ​​to function parameters, note that it is not a reference
  • Return the local object by value, note that it is not a reference
class Person {
    
    
public:
	//默认(无参)构造
	Person() {
    
    
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	//有参构造
	Person(int age) {
    
    
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	//拷贝构造,将传过来的对象的数据全部拷贝一份
	Person(const Person& p) {
    
    
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {
    
    
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};
//拷贝函数调用时机
//1. 使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
    
    

	Person man(100); //p对象已经创建完毕
	Person newman(man); //调用拷贝构造函数
	Person newman2 = man; //拷贝构造

	//Person newman3;
	//newman3 = man; //不是调用拷贝构造函数,赋值操作
}

//2. 值传递的方式给函数参数传值
//相当于Person p1 = p; 
void doWork(Person p1) {
    
    }
void test02() {
    
    
	Person p; //无参构造函数
	//这里会调用拷贝构造函数,实参传给形参的时候会调用拷贝构造,但是doWork里面的p不会改变 这里定义的p,因为拷贝的是临时的副本,不一个内存空间
	doWork(p);
}

//3. 以值方式返回局部对象,局部对象有个特点,函数执行完立即释放
Person doWork2()
{
    
    
	Person p1;
	cout << (int *)&p1 << endl;
	//以值的方式返回,这里返回的并不是p1本身,而是根据p1来创建一个新的对象再返回
	return p1;
}

void test03()
{
    
    
	//函数执行完会调用析构函数
	Person p = doWork2();
	cout << (int *)&p << endl;
}


int main() {
    
    

	//test01();
	//test02();
	test03(); 	//执行完之后会有两个拷贝构造和两个析构函数打印出来。这里的析构函数要等程序运行完才会执行
	system("pause");
	return 0;
}

Constructor calling rules:
By default, as long as a class is written, c++the compiler will add at least one class3 functions

1. Default constructor (no parameters, function body is empty)
2. Default destructor (no parameters, function body is empty)
3. The default copy constructor, which copies the value of the attribute, even if it is not written by itself, the compiler will write it by itself.

For example, in the following code, even if the copy constructor is not written, the Person p2(p1);copy constructor will be automatically created in the class when this sentence is executed, and p1the attribute will be assigned to p2, and the compiler will make a copy of the attribute

void test01()
{
    
    
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

	cout << "p2的年龄为: " << p2.age << endl;
}
  • If a constructor with parameters is written in the class, but a default (no parameter) constructor is not written, the compiler will not provide a default constructor without parameters. If the default constructor is called, an error will be reported, but if no copy construction is written, it will still A copy constructor will be provided, an example has been given above. That is, after writing a parameterized structure, the compiler no longer provides a default structure.
  • If only the copy constructor is written in the class, but the default constructor and the parameterized constructor are not written, then the compiler will no longer provide it. If the default constructor and the parameterized constructor are called again, an error will be reported. That is, after writing the copy construction, the other one is no longer provided.

Take an example of the above question:

class Person {
    
    
public:
	//无参(默认)构造函数
	Person() {
    
    
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int a) {
    
    
		age = a;
		cout << "有参构造函数!" << endl;
	}
	//拷贝构造函数
	Person(const Person& p) {
    
    
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}
	//析构函数
	~Person() {
    
    
		cout << "析构函数!" << endl;
	}
public:
	int age;
};

void test01()
{
    
    
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

	cout << "p2的年龄为: " << p2.age << endl;
}

void test02()
{
    
    
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	Person p1; //此时如果用户自己没有提供默认构造,会出错
	Person p2(10); //用户提供的有参
	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

	//如果用户提供拷贝构造,编译器不会提供其他构造函数
	Person p4; //此时如果用户自己没有提供默认构造,会出错
	Person p5(10); //此时如果用户自己没有提供有参,会出错
	Person p6(p5); //用户自己提供拷贝构造
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

Summary:
Default (no parameter) construction, construction with parameters, and copy construction are three constructors. Only the latter ones are provided. As long as the previous constructors are not written by themselves, the compiler will not provide them.

2.4.5. Deep copy and shallow copy

This knowledge point is very important, and it is often asked during interviews.

  • Shallow copy : simple assignment copy operation in the compiler
  • Deep copy : re-apply for space in the heap area and perform copy operations
class Person {
    
    
public:
	//无参(默认)构造函数
	Person() {
    
    
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int age ,int height) {
    
    
		
		cout << "有参构造函数!" << endl;
		m_age = age;
		m_height = new int(height);
		
	}
	//拷贝构造函数  
	/*
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_age = p.m_age;
		m_height = new int(*p.m_height);
		
	}
	*/

	//析构函数
	~Person() {
    
    
	//析构函数通常用来释放堆区中的数据
		cout << "析构函数!" << endl;
		if (m_height != NULL)
		{
    
    
			delete m_height;
		}
	}
public:
	int m_age;
	//创建一个指针为了上面把身高放在堆区。
	int* m_height;
};

void test01()
{
    
    
	Person p1(18, 180);
	Person p2(p1);

	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;
	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

If you run the above code,
insert image description here
you will find the following results: Let’s see why this error is reported: The reason
insert image description here
  why the above code has a problem is because there is a problem with the height, which is created in the heap area.p1 The address of the height stored in the heap area is copied to the bottom, and the data in the heap area is copied to by default when p1the object is created , that is, the data in the heap area is copied to . After the program is finished running, it needs to execute the destructor to release the sum , but the sum is placed on the stack, the data in the stack is released first, and then the data in the stack is released. At this time, the data in the heap area is repeatedly released, and a conflict. The reason for the problem is caused by the shallow copy of the copy constructor? How to solve it? This problem can be solved by using the deep test. It can be solved by canceling the above comments, implementing the copy constructor by yourself, solving the problems caused by shallow copy, and not using the system's default construction copy. As shown in the code in the figure above, re-open a heap area to store the copied data. Summarize:p2p1p2p2p1p2p1p2p2p1/* */
insert image description here

If the attribute is opened in the heap area , you must provide a copy constructor yourself to prevent problems caused by shallow copying

2.4.6. Initialization list

C++ provides an initializer list syntax for initializing properties. We recommend this way of writing in engineering. Note the placement of the colons.
grammar:构造函数():属性1(值1),属性2(值2)... {}

class Person {
    
    
public:

	传统方式初始化,通常我们都是使用这种有参构造初始化,但是不推荐
	//Person(int a, int b, int c) {
    
    
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}

	//初始化列表方式初始化,工程中推荐用法,这样也可以在构造函数里面写一下其他的语法,这里一定注意冒号的位置
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) //相当于m_A = a, m_B = b, m_C = c
	{
    
    
	
	}
	void PrintPerson() {
    
    
		cout << "mA:" << m_A << endl;
		cout << "mB:" << m_B << endl;
		cout << "mC:" << m_C << endl;
	}
private:
	int m_A;
	int m_B;
	int m_C;
};

int main() {
    
    

	Person p(1, 2, 3);
	p.PrintPerson();


	system("pause");

	return 0;
}

2.4.7. Class objects as members of classes

C++A member of a class can be an object of another class, we call this member an object member . In the relationship between the point and the circle above, it has actually been shown once.

class Phone
{
    
    
public:
	Phone(string name)
	{
    
    
		m_PhoneName = name;
		cout << "Phone构造" << endl;
	}

	~Phone()
	{
    
    
		cout << "Phone析构" << endl;
	}

	string m_PhoneName;

};

class Person
{
    
    
public:

	//初始化列表可以告诉编译器调用哪一个构造函数
	/*Phone是一个类,这里给pName传过来的是个字符串,但是没有报错是因为什么呢?因为这里相当于隐式转换法
	Phone m_Pname = pName。这里就展示了类对象也可以用初始化列表的方式赋初值。*/
	Person(string name, string pName) :m_Name(name), m_Phone(pName)
	{
    
    
		cout << "Person构造" << endl;
	}

	~Person()
	{
    
    
		cout << "Person析构" << endl;
	}

	void playGame()
	{
    
    
		cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;
	}

	string m_Name;
	Phone m_Phone;

};
void test01()
{
    
    
	//当类中成员是其他类对象时,我们称该成员为对象成员
	//构造的顺序是 :先调用对象成员的构造,再调用本类构造
	//析构顺序与构造相反
	Person p("张三" , "苹果X");
	p.playGame();

}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

Summary:
Implicit type conversion occurs when passing

2.4.8. Static members

Static members are divided into:
Static member variables:

  • All objects share the same data
  • Memory is allocated during the compilation phase, and the memory is allocated before the program runs. In the global area
  • In-class declaration, out-of-class initialization
  • can be accessed by class name

Static member function:

  • All objects share the same function
  • Static member functions can only access static member variables
  • can be accessed by class name

First look at the static member variables:

class Person
{
    
    
	
public:
	//静态成员变量特点:
	//1 在编译阶段分配内存
	//2 所有对象共享同一份数据
	//3 类内声明,类外初始化,必须要做的,否则没法访问
	static int m_A; //静态成员变量

};
//类外初始化,注意初始化的时候作用域
int Person::m_A = 10;

void test01()
{
    
    
	//静态成员变量两种访问方式

	//1、通过对象
	Person p1;
	p1.m_A = 100;
	cout << "p1.m_A = " << p1.m_A << endl;

	Person p2;
	p2.m_A = 200;
	cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据,输出200
	cout << "p2.m_A = " << p2.m_A << endl;//输出200
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

The above in-class declaration and out-of-class initialization mean that it needs to be defined in the class and assigned outside the class, but pay attention to which class the declaration space is under.
insert image description here
Look at the complete code, pay attention to the comment section:

class Person
{
    
    
	
public:

	static int m_A; //静态成员变量

	//静态成员变量特点:
	//1 在编译阶段分配内存
	//2 类内声明,类外初始化
	//3 所有对象共享同一份数据

private:
	static int m_B; //静态成员变量也是有访问权限的,外部无法访问
};
int Person::m_A = 10;
int Person::m_B = 10;

void test01()
{
    
    
	//静态成员变量不属于某个对象,所有对象都共享同一份数据
	//静态成员变量两种访问方式
	//1、通过对象,其实这里创不创建对象没任何意义,因为静态成员本身不属于任何对象
	Person p1;
	p1.m_A = 100;
	cout << "p1.m_A = " << p1.m_A << endl;

	Person p2;
	p2.m_A = 200;
	cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
	cout << "p2.m_A = " << p2.m_A << endl;

	//2、通过类名,因为静态成员本身不属于任何对象
	cout << "m_A = " << Person::m_A << endl;


	//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

Let's look at the static member function again:

  • Programs share a function
  • Static member functions can only access static member variables
    and can be accessed through the class name

insert image description here
  Why don't static member functions give access to non-static member properties? Because the data of the static member function has only one copy in memory, the above m_Bis not a non-static member variable, it must be accessed through the object, when we call the above static function, the inside of the function body does not know which object is changed below of m_B. For example, two objects are created p1, p2, p1to call the above static member function, there is one in the function m_B=200, in the body of the static function, m_BI don’t know p1how to change it 200, although p1the static member function is being called, but in the body of the function is not It does not reflect that this is p1a member, and it p2is the same for the same reason. It m_Acan be because he does not belong to any object.

Static member functions also have access rights:

class Person
{
    
    

public:

	//静态成员函数特点:
	//1 程序共享一个函数
	//2 静态成员函数只能访问静态成员变量
	
	static void func()
	{
    
    
		cout << "func调用" << endl;
		m_A = 100;
		//m_B = 100; //错误,不可以访问非静态成员变量
	}

	static int m_A; //静态成员变量
	int m_B; // 
private:

	//静态成员函数也是有访问权限的,类外无法访问
	static void func2()
	{
    
    
		cout << "func2调用" << endl;
	}
};
int Person::m_A = 10;


void test01()
{
    
    
	//静态成员变量两种访问方式

	//1、通过对象,没特殊意义,因为函数不属于任何对象
	Person p1;
	p1.func();

	//2、通过类名,因为函数不属于任何对象,可以直接通过类名访问
	Person::func();


	//Person::func2(); //类外访问不到私有权限静态成员函数
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

2.4.9. C++ object model and this pointer

2.4.9.1. Member variables and member functions are stored separately

  1. In C++, member variables and member functions in a class are stored separately
  2. onlynon-static member variableOn objects that belong to a class, like static member variables, static member functions do not belong to a certain object, and non-static member functions do not belong to a class object. There is only one copy of non-static member functions, which are stored separately from static member variables, that is Functions do not belong on class objects.
class Person {
    
    
public:
	Person() {
    
    
		mA = 0;
	}
	//非静态成员变量占对象空间
	int mA;
	//静态成员变量不占对象空间
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例
	void func() {
    
    
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
    
    
	}
};
int main() {
    
    
	//一个空对象的占员工内存空间是1个字节,即C++编译器会为每个空对象分配一个字节空间,是为了区分空对象的占内存的位置
	//每个空对象也应该有一个独一无二的内存地址
	cout << sizeof(Person) << endl;
	system("pause");
	return 0;
}

  When there is no member in a single object, the compiler will open itone byteSpace. When there is a non-static member variable in it, the object occupies the number of bytes of the member variable, that is, memory is allocated according to the member variable. This non-static member variable belongs to the object of the class. If there are static member variables in the class, then the static member variables do not belong to the class object, and the class object will not open up memory for it. If there is a non-static member function in the class, it does not belong to the class object and does not allocate memory for it. If there is a static member function in the class, it does not belong to the class object, nor will memory be allocated for it. What I want to say above is that member variables and member functions are stored separately.
insert image description here
Ultimately, onlynon-static member variablebelongs to the class object. No memory is allocated on objects that do not belong to the class.

2.4.9.2. this pointer

  Just now we said that some members defined in the class object do not belong to the class object, so how do we distinguish which object is in use when creating different objects?
  We know that C++member variables and member functions are stored separately, and each non-static member function will only generate a function instance, which means that multiple objects of the same type will share a piece of code

So the question is: how does this piece of code distinguish which object calls itself?

c++By providing a special object pointer, thispointer ( a meaning Pythonin the root self), solve the above problems. The this pointer points to the object to which the called member function belongs , and whoever calls it points to whom .
thisA pointer is a pointer that is implied in each non-static member function. The non-static member function has a this pointer by default. It can be used directly without defining it yourself.
thisThe pointer does not need to be defined, it can be used directly to use
the this pointer:

  • When the formal parameter and the member variable have the same name, they can be thisdistinguished by pointers. resolve name conflicts
  • To return the object itself in a non-static member function of a class, usereturn *this. return the object itself

let's take a lookname conflictAn example of:

class Person
{
    
    
public:
	/*
	这里在编译的时候不会出现错误,但是最终的程序输出结果不是10,这里想做的是把传过来的值赋值给成员变量age,
	但是呢成员变量和age同名,出现了名称冲突。规范写法应该是区分开来,成员属性前面加个m,即m_age,表示成员属性,
	即下面的/**/注释部分写法。或者通过this指针去解决。
	 */
	Person(int age)
	{
    
    
		age = age;
	}
	int age;
	/*
		Person(int age)
	{
		m_age = age;
	}
	int m_age;*/
};

void test01()
{
    
    
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

insert image description here
The second solution: by thissolving.

class Person
{
    
    
public:
	Person(int age)
	{
    
    
		//1、当形参和成员变量同名时,可用this指针来区分
		//this指针指向被调用成员函数所属的对象,此时的this就相当于p1.
		this->age = age;
	}
	int age;
};

void test01()
{
    
    
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

see belowreturn objectUse by itself *this:

class Person
{
    
    
public:

	Person(int age)
	{
    
    
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}
	/*注意,这里返回的是引用,返回的还是对象本身,如果这里返回的不是引用直接Person而不是Person&,即直接返回值,那么下面的代码
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);输出结果还是20。为什么呢?因为如果这里返回的是值的
	话,在调用完p2.PersonAddPerson(p1)第一次之后p2的年龄加了10岁,这里没问题,但是呢这里返回的已经不是p2的本体了,而是按照本
	体创建了一个新的数据调用了拷贝构造函数,我们知道在拷贝构造函数调用构造函数时,用值的方式返回会复制一份新的数据出来,相当于这
	里的person已经和自身的不一样了。即每次返回的都是一个新的东西,跟原来已经不一样了。*/
	Person& PersonAddPerson(Person p)
	{
    
    
		this->age += p.age;
		//返回对象本身
		//this指向对象本身,而*this指向的是p2这个对象本体
		return *this;
	}

	int age;
};

void test01()
{
    
    
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	//上面如果改成返回值的话这里输出就是20,因为值返回是拷贝构造创建新的对象,值一直都是10,加10返回就是20。
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

insert image description here
The difference between function return value and return reference: returning by value will copy a new data, while reference returns itself. Chain programming ideas.

2.4.9.3. Null pointer access member function

In C++, the null pointer can also call the member function, but also pay attention to whether the this pointer is used.
If thisthe pointer is used, it needs to be judged to ensure the robustness of the code.

//空指针访问成员函数
class Person {
    
    
public:

	void ShowClassName() {
    
    
		cout << "我是Person类!" << endl;
	}

	void ShowPerson() {
    
    
		//加上这句话,防止下面空指针代码出错
		if (this == NULL) {
    
    
			return;
		}
		//其实这里的mAge默认的是this->mAge,而传入的指针是NULL,不指向任何东西,这里会报错。
		cout << mAge << endl;
	}

public:
	int mAge;
};

void test01()
{
    
    
	//空指针,没有指向任何确切的对象
	Person * p = NULL;
	p->ShowClassName(); //空指针,可以调用成员函数,单独调用这行代码是不会出错的
	//调用这行代码会出错
	p->ShowPerson();  //但是如果成员函数中用到了this指针,就不可以了
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

From the above code, it can be seen that the null pointer can access class members, but be careful when using it. When no member attribute is used in the class, a null pointer can be used to access the class member.

2.4.9.4. const modified member function

Constant function:

  • After the member function is added, constwe call this function a constant function
  • Member attributes cannot be modified in constant functions
  • After adding keywords when declaring member properties mutable, they can still be modified in constant functions

Constant object:

  • constThe object is called a constant object before declaring the object
  • Constant objects can only call constant functions

Let's look at the constant function first, this place is a bit confusing, see the following code and comments:

class Person {
    
    
public:

	//this指针的本质是一个指针常量,指针的指向不可修改
	//如果想让指针指向的值也不可以修改,需要声明常函数
	//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
	void ShowPerson() const 
	{
    
    

		//this = NULL; //this指针是不能修改指针的指向的,但是指向的值是可以修改的,this的本意相当于:Person* const this;
		/*
		但是this指针指向的对象的数据是可以修改的,这里的前提是void ShowPerson() const中的const没加,如果加了也不能修改值了。
		总结:
		如果不加const呢,在这里的this指针就相当于Person * const this,指向不可修改,但是指向的值可以修改,
		如果加了const呢,就相当于const Person * const this,即指向不能改,指向的内容也不能改,
		那么这个const加在哪合适呢,编译器想来想去还是加载函数后面吧,于是就有了void ShowPerson() const*/
		this->mA = 100; 
		//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
		//变量前面加了mutable,表示可以修改
		//this->m_B = 100;
	}
	
public:
	int m_A;
	mutable int m_B; //可修改 可变的,即使在常函数中也可以修改
};


//const修饰函数
void test01() {
    
    

	Person p;   
	//当利用p去调用成员函数ShowPerson()的时候,类中的this就指向这个p。
	p.ShowPerson();
	cout << person.m_A << endl;
	p.showPerson();

}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

The above code shows that thisthe essence of the pointer is apointer constant, the pointing of the pointer cannot be modified.
Let's look at constthe modified constant object again :

class Person {
    
    
public:
	Person() {
    
    
		m_A = 0;
		m_B = 0;
	}

	//this指针的本质是一个指针常量,指针的指向不可修改
	//如果想让指针指向的值也不可以修改,需要声明常函数
	void ShowPerson() const {
    
    
		//const Type* const pointer;
		//this = NULL; //不能修改指针的指向 Person* const this;
		//this->mA = 100; //但是this指针指向的对象的数据是可以修改的,加上了const就不能改了

		//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
		this->m_B = 100;
	}

	void MyFunc() {
    
    
		//mA = 10000;
	}

public:
	int m_A;
	mutable int m_B; //可修改 可变的
};


//const修饰对象  常对象
void test01() {
    
    

	const Person person; //在对象前加上const变为常量对象,不允许修改指针指向的值,对象的属性不允许修改
	cout << person.m_A << endl;
	//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
	person.m_B = 100; //但是常对象可以修改mutable修饰成员变量

	//常对象访问成员函数
	person.MyFunc(); //常对象能调用const的函数,不能调用普通成员函数,因为普通成员函数可以修改成员变量

}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

  The member attribute cannot be modified in the constant function. The reason is that the constant function modifies the pointer const, thisand thisthe pointer itself is a pointer constant, so after adding it, cosnteven the value pointed to by the pointer cannot be modified. If you insist on modifying, you can add it mutable. Constant objects can only call constant functions.

2.4.10.Tomomoto

  In the program, some private attributes also want to be accessed by some special functions or classes outside the class, so you need to use the technology of friends. A friend is to declare some special functions or some special classes to access the private members of this class as a good friend of another class.
The keyword for a friend isfriend
There are three implementation methods of friends:

  • Global functions as friends
  • class as friend
  • member function as friend

Global functions as friends:

class Building
{
    
    
	//告诉编译器 goodGay全局函数 是 Building类的好朋友,就可以访问类中的私有内容了
	friend void goodGay(Building * building);

public:

	Building()
	{
    
    
		this->m_SittingRoom = "客厅";
		this->m_BedRoom = "卧室";
	}

public:
	string m_SittingRoom; //客厅

private:
	string m_BedRoom; //卧室
};

//全局函数
void goodGay(Building * building)
{
    
    
	cout << "好基友正在访问: " << building->m_SittingRoom << endl;
	//上面类里面如果没有friend void goodGay(Building * building)这个声明就会报错,无法访问私有属性。
	cout << "好基友正在访问: " << building->m_BedRoom << endl;
}


void test01()
{
    
    
	Building b;
	goodGay(&b);
}

int main(){
    
    

	test01();

	system("pause");
	return 0;
}

As you can see above, as long as you put the global function in front of the class friend, you can access the private attributes in the
class. The class is a friend:
the purpose of a friend is to allow a class to access private members in another class.

class Building; //为了防止下面写Building类的时候报错
class goodGay
{
    
    
public:
	//函数具体内容写在外面,这样代码看着更简洁
	goodGay();
	void visit();
private:
	Building *building;
};

class Building
{
    
    
	//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容
	friend class goodGay;
public:
	Building();

public:
	string m_SittingRoom; //客厅
private:
	string m_BedRoom;//卧室
};
//在类外声明构造函数,注意加上作用于,哪个类下面的。下面使用了this指针,如果使用初始化列表的方式就不能使用this指针了
Building::Building()
{
    
    
	this->m_SittingRoom = "客厅";
	this->m_BedRoom = "卧室";
}
//类外声明构造函数,注意哪个类下面的
goodGay::goodGay()
{
    
    
	building = new Building;
}
//类外声明成员函数,注意哪个类下面的
void goodGay::visit()
{
    
    
	cout << "好基友正在访问" << building->m_SittingRoom << endl;
	cout << "好基友正在访问" << building->m_BedRoom << endl;
}

void test01()
{
    
    
	goodGay gg;
	gg.visit();
}

int main(){
    
    
	test01();
	system("pause");
	return 0;
}

There is an implicit note above, about whether it can be used when the constructor is initialized, thisyou can refer to the following two blog posts:

Member functions as friends:

class Building;
class goodGay
{
    
    
public:
	goodGay();
	void visit(); //只让visit函数作为Building的好朋友,可以发访问Building中私有内容
	void visit2(); 
private:
	Building *building;
};

class Building
{
    
    
	//告诉编译器  goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容
	friend void goodGay::visit();
public:
	Building();

public:
	string m_SittingRoom; //客厅
private:
	string m_BedRoom;//卧室
};

Building::Building()
{
    
    
	this->m_SittingRoom = "客厅";
	this->m_BedRoom = "卧室";
}

goodGay::goodGay()
{
    
    
	building = new Building;
}

void goodGay::visit()
{
    
    
	cout << "好基友正在访问" << building->m_SittingRoom << endl;
	cout << "好基友正在访问" << building->m_BedRoom << endl;
}

void goodGay::visit2()
{
    
    
	cout << "好基友正在访问" << building->m_SittingRoom << endl;
	//cout << "好基友正在访问" << building->m_BedRoom << endl;
}

void test01()
{
    
    
	goodGay  gg;
	gg.visit();
}

int main(){
    
    
	test01();
	system("pause");
	return 0;
}

2.4.11. Operator overloading

Plus operator overloading:
  operator overloading concept: redefine the existing operator and give it another function to adapt to different data types. For example, if I want to; add two people, what does it mean to add two people? Look at the legend below: To add two objects, we created a Person AddPersonmember/global function, but everyone has a different name, so the compiler gave a common name operator+, using the function from the compiler The name can simplify the calling method and be used directly +. It should be noted that if overloading is implemented through a global function, two variables need to be passed in.
insert image description here
Let's look at an example:
insert image description here
What should I do if the above error is reported? It needs to be solved by overloading.

class Person {
    
    
public:
	Person(int a, int b)
	{
    
    
		this->m_A = a;
		this->m_B = b;
	}
	//成员函数实现 + 号运算符重载
	Person operator+(const Person& p)
	{
    
    
		//创建一个临时的Person变量
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}

public:
	int m_A;
	int m_B;
};

//全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {
    
    
//	Person temp(0, 0);
//	temp.m_A = p1.m_A + p2.m_A;
//	temp.m_B = p1.m_B + p2.m_B;
//	return temp;
//}

//运算符重载 可以发生函数重载 ,实现一个对象加一个整数
Person operator+(const Person& p2, int val)  
{
    
    
	Person temp;
	temp.m_A = p2.m_A + val;
	temp.m_B = p2.m_B + val;
	return temp;
}

void test() {
    
    

	Person p1(10, 10);
	Person p2(20, 20);

	//成员函数方式
	//成员函数的本质调用方法:Person p3 = p1.operator+(p2);
	//简化方法
	Person p3 = p2 + p1;  //相当于 p2.operaor+(p1)
	cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;
	
	//全局函数重载的本质调用:Person p3 = operator+(p1,p2)
	Person p4 = p3 + 10; //相当于 Person p4 = operator+(p3,10)
	cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;
	
	//运算符重载也可以发生函数重载
	Person p4 = p3+10;
}

int main() {
    
    
	test();
	system("pause");
	return 0;
}

Summary 1: It is impossible to change the operators of expressions of built-in data types. For example, two integers 1+1=2 cannot be turned into 1-1=0.
Summary 2: Do not abuse operator overloading. For example, operator+ is obviously an addition operation, but the function of the code is subtraction.

Left shift operator:
  << Overloading can output custom content. For example, if you want to directly output the member variables in the class name output, it is normally impossible, but you can do it through overloading.
insert image description here

class Person {
    
    
	friend ostream& operator<<(ostream& out, Person& p);

public:

	Person(int a, int b)
	{
    
    
		this->m_A = a;
		this->m_B = b;
	}

	/*如果这里参数写的是Person& p,最后调用的时候变成p.operator<<(p),简化版本变成了p<<p,
	不是我们要的。那如果传cout呢(cout也是对象,可以传),最后调用的时候变成p.operator<<(cout),
	简化版本为p<<cout,也不是我们想要的结果。成员函数无法实现移位运算符的重载*/
	//void operator<<(Person& p){
    
    
	//}

private:
	int m_A;
	int m_B;
};

//只能全局函数实现左移重载
/*
ostream(输出流)对象,全局只能有一个,必须用引用方式传过来。注意这里的返回类型ostream。重载的核心operator<<。
这里还要注意到友元,私有类型。这里返回ostream才可以无限使用<<输出,不然只能使用一次。
这里的使用的是引用,cout是别名,可以换成其他的名字。这里关于cout可以是别名仍有疑问,待更近一步研究(第二遍的理解:cout在可以理解成内部定义好的关键词,只有一个,全局成员函数只是通过引用的方式接收他而已,所以想起什么名字起什么名字)。
*/
//注意,这里cout还必须是引用,因为全局只有一个cout对象
ostream& operator<<(ostream& cout, Person& p) {
    
    
	cout << "a:" << p.m_A << " b:" << p.m_B;
	return cout;
}

void test() {
    
    

	Person p1(10, 20);
	cout << p1 << "hello world" << endl; //链式编程,因为自己实现了重载,因此可以实现cout << p1也可以实现cout << "hello world",相当于调用的函数不一样。
}

int main() {
    
    

	test();
	system("pause");
	return 0;
}

Summary: Overloading the left shift operator with friends can realize the output of custom data types. Overloading cannot be achieved through member functions.

Overloading of the increment operator:
Function: Realize your own integer data by overloading the increment operator
insert image description here

class MyInteger {
    
    

	friend ostream& operator<<(ostream& out, MyInteger myint);
public:
	MyInteger() {
    
    
		m_Num = 0;
	}
	//前置++,注意这里返回的是引用而不是值,如果返回值的话只能执行一次++,不可以连续多个++,如++(++a)仍然只做一次++。
	MyInteger& operator++() {
    
    
		//先++
		m_Num++;
		//再返回
		return *this;
	}

	//后置++,这里不能返回引用,如果返回的是引用相当于返回的是局部对象的引用,局部对象这里运行完立即释放,后面在操作就是非法的,这里有个占位参数int,只能写int,可以区分前置和后置,不然重载错误
	MyInteger operator++(int) {
    
    
		//先记录
		MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1				     	  
		//后递增
		m_Num++;
		return temp;
	}
 
private:
	int m_Num;
};

ostream& operator<<(ostream& out, MyInteger myint) {
    
    
	out << myint.m_Num;
	return out;
}

//前置++ 先++ 再返回
void test01() {
    
    
	MyInteger myInt;
	cout << ++myInt << endl;
	cout << myInt << endl;
}

//后置++ 先返回 再++
void test02() {
    
    

	MyInteger myInt;
	cout << myInt++ << endl;
	cout << myInt << endl;
}

int main() {
    
    
	test01();
	//test02();
	system("pause");
	return 0;
}

Summary: pre-increment return reference, post-increment return value

Assignment operator overloading:
The C++ compiler actually adds a total of 4 functions to at least one class

  1. Default constructor (no parameters, function body is empty)
  2. Default destructor (no parameters, function body is empty)
  3. The default copy constructor, which copies the value of the property
  4. Assignment operator operator=, copy the value of the property
class Person
{
    
    
public:
	Person(int age)
	{
    
    
		//将年龄数据开辟到堆区
		m_Age = new int(age);
	}

	//重载赋值运算符 
	Person& operator=(Person &p)
	{
    
    
	//先判断是否释放干净
		if (m_Age != NULL)
		{
    
    
			delete m_Age;
			m_Age = NULL;
		}
		//编译器提供的代码是浅拷贝,析构函数释放的时候会报错。在上面的拷贝构造函数中的浅拷贝已经解释过了。
		//m_Age = p.m_Age;

		//提供深拷贝 解决浅拷贝的问题
		m_Age = new int(*p.m_Age);

		//返回自身,自由返回自身才能多次赋值
		return *this;
	}

	~Person()
	{
    
    
		if (m_Age != NULL)
		{
    
    
			delete m_Age;
			m_Age = NULL;
		}
	}
	//年龄的指针
	int *m_Age;
};

void test01()
{
    
    
	Person p1(18);
	Person p2(20);
	Person p3(30);
	//p2 = p1; //赋值操作,将p1的所有数据赋值给p2,此时p1和p2中的堆中的数据指向同一个位置,如果析构函数中使用的是浅拷贝将会报错,内存释放两次。
	p3 = p2 = p1; 
	cout << "p1的年龄为:" << *p1.m_Age << endl;
	cout << "p2的年龄为:" << *p2.m_Age << endl;
	cout << "p3的年龄为:" << *p3.m_Age << endl;
}

int main() {
    
    
	test01();
	//int a = 10;
	//int b = 20;
	//int c = 30;
	//链式操作,我们的重载也应该要满足这个条件
	//c = b = a;
	//cout << "a = " << a << endl;
	//cout << "b = " << b << endl;
	//cout << "c = " << c << endl;
	system("pause");
	return 0;
}

If there are attributes in the class pointing to the heap area, there will also be deep and shallow copy problems when doing assignment operations

Relational operator overloading:
Function : Overloading relational operators, allowing two custom type objects to perform comparison operations

class Person
{
    
    
public:
	Person(string name, int age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	};
	bool operator==(Person & p)
	{
    
    
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
    
    
			return true;
		}
		else
		{
    
    
			return false;
		}
	}

	bool operator!=(Person & p)
	{
    
    
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
    
    
			return false;
		}
		else
		{
    
    
			return true;
		}
	}
	string m_Name;
	int m_Age;
};

void test01()
{
    
    
	//int a = 0;
	//int b = 0;
	Person a("孙悟空", 18);
	Person b("孙悟空", 18);

	if (a == b)
	{
    
    
		cout << "a和b相等" << endl;
	}
	else
	{
    
    
		cout << "a和b不相等" << endl;
	}
	
	if (a != b)
	{
    
    
		cout << "a和b不相等" << endl;
	}
	else
	{
    
    
		cout << "a和b相等" << endl;
	}
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

Function call operator overloading:

  • The function call operator () can also be overloaded
  • Because the method used after overloading is very similar to the call of a function, it is called a functor
  • Functor has no fixed way of writing and is very flexible
class MyPrint
{
    
    
public:
	void operator()(string text)
	{
    
    
		cout << text << endl;
	}
};
void test01()
{
    
    
	//重载的()操作符 也称为仿函数
	MyPrint myFunc;
	myFunc("hello world");
}

class MyAdd
{
    
    
public:
	int operator()(int v1, int v2)
	{
    
    
		return v1 + v2;
	}
};

void test02()
{
    
    
	MyAdd add;
	int ret = add(10, 10);
	cout << "ret = " << ret << endl;

	/*匿名对象调用MyAdd()代替add,执行完立即释放。匿名函数对象MyAdd(),首先他是个匿名对象,
	然后他又重载了(),所以我们叫它仿函数,所以又叫匿名函数对象。*/
	cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}

int main() {
    
    
	test01();
	test02();
	system("pause");
	return 0;
}

2.5. Inheritance

  There is a special relationship between some classes. For example, we see that many websites have a common head, a common bottom, and even a common left list. Only the center content is different. Next, we use ordinary writing and Inheritance is used to implement the contents of the Java, C++ and Python tutorial web pages, and take a look at the meaning and benefits of inheritance.
Common implementation:

//Java页面
class Java 
{
    
    
public:
	void header()
	{
    
    
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
    
    
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left()
	{
    
    
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
    
    
		cout << "JAVA学科视频" << endl;
	}
};
//Python页面
class Python
{
    
    
public:
	void header()
	{
    
    
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
    
    
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left()
	{
    
    
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
    
    
		cout << "Python学科视频" << endl;
	}
};
//C++页面
class CPP 
{
    
    
public:
	void header()
	{
    
    
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
    
    
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left()
	{
    
    
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
    
    
		cout << "C++学科视频" << endl;
	}
};

void test01()
{
    
    
	//Java页面
	cout << "Java下载视频页面如下: " << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "--------------------" << endl;

	//Python页面
	cout << "Python下载视频页面如下: " << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "--------------------" << endl;

	//C++页面
	cout << "C++下载视频页面如下: " << endl;
	CPP cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

As you can see, many of the above codes are repeated.
Let’s look at the usage of inheritance:

//公共页面
class BasePage
{
    
    
public:
	void header()
	{
    
    
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}

	void footer()
	{
    
    
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left()
	{
    
    
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}

};

//Java页面
class Java : public BasePage
{
    
    
public:
	void content()
	{
    
    
		cout << "JAVA学科视频" << endl;
	}
};
//Python页面
class Python : public BasePage
{
    
    
public:
	void content()
	{
    
    
		cout << "Python学科视频" << endl;
	}
};
//C++页面
class CPP : public BasePage
{
    
    
public:
	void content()
	{
    
    
		cout << "C++学科视频" << endl;
	}
};

void test01()
{
    
    
	//Java页面
	cout << "Java下载视频页面如下: " << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "--------------------" << endl;

	//Python页面
	cout << "Python下载视频页面如下: " << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "--------------------" << endl;

	//C++页面
	cout << "C++下载视频页面如下: " << endl;
	CPP cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

As you can see, inherited methods can greatly reduce code duplication.
Summarize:

Benefits of inheritance:Duplicated code can be reduced
Inheritance method:

class A : public B;

Class A is called child class or derived class Class
B is called parent class or base class

Members of derived classes, including two parts :

One class is inherited from the base class, and the other class is a member added by itself.

The ones inherited from the base class show their commonality, while the newly added members reflect their individuality.

Inheritance method:
Inherited syntax:class 子类 : 继承方式 父类

There are three types of inheritance:

  • public inheritance
  • protected inheritance
  • private inheritance

insert image description here
The far left in the above picture is public inheritance, which is wrongly written in the picture.
Let's look at the following three examples:

class Base1
{
    
    
public: 
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

//公共继承
class Son1 :public Base1
{
    
    
public:
	void func()
	{
    
    
		m_A; //可访问父类中的 public权限成员
		m_B; //可访问父类中的 protected权限,到子类中仍然是保护权限,只有类内能访问,类外不能访问
		//m_C; //不可访问,父类中私有权限成员不可访问
	}
};

//创建一个测试函数
void myClass()
{
    
    
	Son1 s1;
	s1.m_A; //其他类只能访问到公共权限
	//s1.m_B;  报错,m_B到了子类Son1中变成了保护权限,类外不能访问保护权限
}

//保护继承
class Base2
{
    
    
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son2:protected Base2
{
    
    
public:
	void func()
	{
    
    
		m_A; //父类中的公共成员,到子类中变成了保护权限
		m_B; //父类中的保护成员,到子类中变成了保护权限
		//父类中的私有成员,不可访问
		//m_C; //不可访问
	}
};
void myClass2()
{
    
    
	Son2 s;
	//报错,m_A到了子类Son2中变成了保护权限,类外不能访问保护权限
	//s.m_A; //不可访问
}

//私有继承
class Base3
{
    
    
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son3:private Base3
{
    
    
public:
	void func()
	{
    
    
		m_A; //父类中的公共成员到子类中变成了私有成员
		m_B; //父类中的保护成员到子类中变成了私有成员
		//m_C; //不可访问
	}
};
class GrandSon3 :public Son3
{
    
    
public:
	void func()
	{
    
    
		//Son3是私有继承,所以即使孙子以公共的方式继承父类Son3的属性在GrandSon3中都无法访问到,会直接报错
		//m_A;
		//m_B;
		//m_C;
	}
};

2.5.1. Object Model in Inheritance

All non-static member properties in the parent class will be inherited, but the private member properties in the parent class are hidden by the compiler, so they cannot be accessed, but they are indeed inherited.

class Base
{
    
    
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};

//公共继承
class Son :public Base
{
    
    
public:
	int m_D;
};

void test01()
{
    
    
	//输出结果是12,父类中的所有非静态成员都别继承下去。父类中的私有属性是被编译器给隐藏了,因此访问不到,但是确实是被继承下去了。
	cout << "sizeof Son = " << sizeof(Son) << endl;
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

You can also use the developer command prompt tool to see which properties are inherited. Specific steps are as follows:
insert image description here
insert image description here

2.5.2. Construction and destruction order in inheritance

class Base 
{
    
    
public:
	Base()
	{
    
    
		cout << "Base构造函数!" << endl;
	}
	~Base()
	{
    
    
		cout << "Base析构函数!" << endl;
	}
};

class Son : public Base
{
    
    
public:
	Son()
	{
    
    
		cout << "Son构造函数!" << endl;
	}
	~Son()
	{
    
    
		cout << "Son析构函数!" << endl;
	}

};


void test01()
{
    
    
	//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
	Son s;
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

insert image description here
If there is inheritance in the subclass, when creating the subclass object, because of the inheritance operation, the subclass will definitely get the properties and members of the parent class, and will also create the parent class object, so it will create the parent class object While calling the parent class object.

Summary: In inheritance, the parent class constructor is called first, and then the subclass constructor is called. The order of destruction is opposite to that of construction.

2.5.3. Inheritance of the same name processing method

class Base {
    
    
public:
	Base()
	{
    
    
		m_A = 100;
	}

	void func()
	{
    
    
		cout << "Base - func()调用" << endl;
	}

	void func(int a)
	{
    
    
		cout << "Base - func(int a)调用" << endl;
	}

public:
	int m_A;
};


class Son : public Base {
    
    
public:
	Son()
	{
    
    
		m_A = 200;
	}

	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	void func()
	{
    
    
		cout << "Son - func()调用" << endl;
	}
public:
	int m_A;
};

void test01()
{
    
    
	Son s;
	//直接访问就是自己类下面的
	cout << "Son下的m_A = " << s.m_A << endl;
	//如果想要访问父类中的同名成员,需要加上作用域
	cout << "Base下的m_A = " << s.Base::m_A << endl;

	s.func();
	s.Base::func();
	/* 如果子类中出现和父类中的同名成员函数,子类中的同名成员函数会隐藏掉父类中所有的同名成员函数即所有的重载函数都被隐藏了。
	只要同名不管是否重载都被隐藏
	如果想访问到父类中的隐藏的同名成员函数,需要加作用域。
	*/
	s.Base::func(10);

}
int main() {
    
    
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
  1. Subclass objects can directly access members of the same name in the subclass
  2. The subclass object plus scope can access the members of the same name of the parent class
  3. When the subclass has a member function with the same name as the parent class, the subclass will hide the member function with the same name in the parent class (including overloading), and add scope to access the function with the same name in the parent class

2.5.4. Inheritance of static members with the same name processing method

  First review the characteristics of static member variables mentioned earlier: all objects share the same data, memory is allocated during compilation, and the characteristics of static member functions declared in the class and initialized outside the class: only static member variables can be accessed, and non-static member variables cannot be
accessed , and all objects share a piece of data.
Question: How can static members with the same name in inheritance be accessed on subclass objects?
Static members and non-static members with the same name are handled in the same way

  • Access to subclass members with the same name can be accessed directly
  • Access to members with the same name of the parent class requires scope
class Base {
    
    
public:
	static void func()
	{
    
    
		cout << "Base - static void func()" << endl;
	}
	static void func(int a)
	{
    
    
		cout << "Base - static void func(int a)" << endl;
	}

	static int m_A;
};

int Base::m_A = 100;

class Son : public Base {
    
    
public:
	static void func()
	{
    
    
		cout << "Son - static void func()" << endl;
	}
	static int m_A;
};

int Son::m_A = 200;

//同名成员属性
void test01()
{
    
    
	//通过对象访问
	cout << "通过对象访问: " << endl;
	Son s;
	cout << "Son  下 m_A = " << s.m_A << endl;
	cout << "Base 下 m_A = " << s.Base::m_A << endl;

	//通过类名访问
	cout << "通过类名访问: " << endl;
	cout << "Son  下 m_A = " << Son::m_A << endl;
	//通过子类对象访问父类的静态成员数据,第一个::表示通过类名的方式访问,第二个::表示表示访问父类作用域下的
	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}

//同名成员函数
void test02()
{
    
    
	//通过对象访问
	cout << "通过对象访问: " << endl;
	Son s;
	s.func();
	s.Base::func();

	cout << "通过类名访问: " << endl;
	Son::func();
	//通过类名访问父类中的func()。
	Son::Base::func();
	//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问,如果下面的Base不加还是报错。
	Son::Base::func(100);
}
int main() {
    
    

	//test01();
	test02();

	system("pause");

	return 0;
}

Summary: The processing method of static members with the same name is the same as that of non-static members, except that there are two access methods (through objects and through class names), and non-static members can only be accessed through objects

2.5.5. Multiple inheritance

C++ allows a class to inherit multiple classes
Grammar: class 子类 :继承方式 父类1 , 继承方式 父类2...
Multiple inheritance may cause members with the same name to appear in the parent class, and scope needs to be added to distinguish it.
In the actual development of C++, it is not recommended to use multiple inheritance

class Base1 {
    
    
public:
	Base1()
	{
    
    
		m_A = 100;
	}
public:
	int m_A;
};

class Base2 {
    
    
public:
	Base2()
	{
    
    
		m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确
	}
public:
	int m_A;
};

//语法:class 子类:继承方式 父类1 ,继承方式 父类2 
class Son : public Base2, public Base1 
{
    
    
public:
	Son()
	{
    
    
		m_C = 300;
		m_D = 400;
	}
public:
	int m_C;
	int m_D;
};


//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
    
    
	Son s;
	//输出16,两个父类里面都继承了
	cout << "sizeof Son = " << sizeof(s) << endl;
	//当父类中出现同名的时候,使用的时候需要加作用域,所以一般不建议使用多继承
	cout << s.Base1::m_A << endl;
	cout << s.Base2::m_A << endl;
}

int main() {
    
    
	test01();
	system("pause");
	return 0;
}

2.5.6. Diamond Inheritance

Diamond inheritance concept:

​ Two derived classes inherit the same base class​, and another class inherits two derived classes at the same time. This kind of inheritance is called diamond inheritance, or diamond inheritance.

Look at an example:

  1. Sheep inherit the data of animals, and camels also inherit the data of animals. When grass mud horses use data, because sheep and camels inherit the same data, ambiguity will arise at this time.
  2. The grass mud horse inherited two copies of the animal data. In fact, we should be clear that we only need one copy of this data.
class Animal
{
    
    
public:
	int m_Age;
};

//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal 
{
    
    

};
class Tuo   : virtual public Animal
{
    
    

};
class SheepTuo : public Sheep, public Tuo
{
    
    

};

void test01()
{
    
    
	SheepTuo st;
	st.Sheep::m_Age = 100;//这里有个注意的地方,::的优先级高于.的优先级,这里注意一下,不然有时候这样写代码看着很迷惑
	st.Tuo::m_Age = 200;
	//当菱形继承时,两个父类具有相同的数据,需要加作用域区分
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
	//这份数据我们知道 只要有一份就可以,菱形继承导致数据有两份,资源浪费。那么这里输出的应该是100还是200呢?怎么解决这个问题?利用虚继承可以解决菱形继承的问题,继承之前 加上关键字 virtual 变为虚继承。这个时候的输出为200,包括上面的输出也变为了200。当我们发生虚继承之后只保留最后一次的,也就是只能有一份数据。
	cout << "st.m_Age = " << st.m_Age << endl;
}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

Take a look with the developer command prompt tool.
insert image description here
insert image description here
The above figure vbptris the virtual base class pointer, which will point to vbtable(virtual base class table)

Summarize:

  • The main problem caused by diamond inheritance is that subclasses inherit two copies of the same data, resulting in waste of resources and meaninglessness
  • Using virtual inheritance can solve the diamond inheritance problem (virtual base class pointer, there will be a virtual function pointer later)
  • Not recommended for use

2.6. Polymorphism

Polymorphism is one of the three object-oriented features of C++

Polymorphism falls into two categories:

  • Static polymorphism:function overloadingandoperator overloadingBelongs to static polymorphism, reuse function name
  • Dynamic polymorphism: derived classes and virtual functions implement runtime polymorphism,Usually we refer to polymorphism as dynamic polymorphism

The difference between static polymorphism and dynamic polymorphism:

  • Static polymorphic function addressearly binding- The compile phase determines the function address
  • Dynamic polymorphic function addresslate binding- The runtime determines the function address

insert image description here
In C++, type conversion between parent and child is allowed without mandatory type conversion, and the pointer or reference of the parent class can directly point to the object of the subclass.
The above code output is the animal talking , because now the address of our function belongs to the address binding, the incoming object has been bound to the Animaladdress, and the address of the function has been determined during the compilation stage. If you want to execute Let the cat talk, then the address of this function cannot be bound in advance, it needs to be bound during the running phase, and the address is bound later. How to do it? It is to add it in front of the function of the parent class virtual. After adding this, virtualthe following subclasses can realize the address late binding after implementing the function with the same name as the parent class.

Polymorphism satisfies the conditions:

1. There is an inheritance relationship
2. The subclass overrides the virtual function in the parent class, and the virtual keyword can be written or not

Polymorphic use:

A parent class pointer or reference points to a subclass object

See the example below:

class Animal
{
    
    
public:
	//Speak函数就是虚函数
	//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
	virtual void speak()
	{
    
    
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal
{
    
    
public:
	void speak()
	{
    
    
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
    
    
public:

	void speak()
	{
    
    
		cout << "小狗在说话" << endl;
	}

};
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编

void DoSpeak(Animal & animal)
{
    
    
	animal.speak();
}
//
//动态多态满足条件: 
//1、有继承关系
//2、子类重写父类中的虚函数,virtual关键字可写可不写
//多态使用:
//父类指针或引用指向子类对象

void test01()
{
    
    
	Cat cat;
	DoSpeak(cat);
	
	Dog dog;
	DoSpeak(dog);
}


int main() {
    
    
	test01();
	system("pause");
	return 0;
}

Dynamic polymorphism satisfies the conditions:

1. There is an inheritance relationship
2. SubclassesOverride the virtual function in the parent class, virtualthe keyword can be written or not

Polymorphic use:

A parent class pointer or reference points to a subclass object

2.6.1. Low-level principles of polymorphism

insert image description here
  The core of the above figure is the virtual function table, and the virtual function table in the subclass will be replaced with the function address of the subclass. There is a virtual function table pointer in the parent class , which points to the virtual function in the parent class. When the subclass rewrites the function in the parent class, the reference or pointer of the parent class object points to the subclass, and polymorphism occurs. At this time, the pointer is points to the subclass. Pay attention to the difference between the distinction and the virtual base class pointer above.
Advantages of polymorphism:

  • Code organization is clear
  • Readable
  • Conducive to early and late expansion and maintenance

Let's take a look at a case to implement a calculator:

//普通实现
class Calculator {
    
    
public:
	int getResult(string oper)
	{
    
    
		if (oper == "+") {
    
    
			return m_Num1 + m_Num2;
		}
		else if (oper == "-") {
    
    
			return m_Num1 - m_Num2;
		}
		else if (oper == "*") {
    
    
			return m_Num1 * m_Num2;
		}
		//如果要提供新的运算,需要修改源码
	}
public:
	int m_Num1;
	int m_Num2;
};

void test01()
{
    
    
	//普通实现测试
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;
	cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;

	cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;

	cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}

//多态实现
//抽象计算器类
//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
class AbstractCalculator
{
    
    
public :

	virtual int getResult()
	{
    
    
		return 0;
	}

	int m_Num1;
	int m_Num2;
};

//加法计算器
class AddCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 + m_Num2;
	}
};

//减法计算器
class SubCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 - m_Num2;
	}
};

//乘法计算器
class MulCalculator :public AbstractCalculator
{
    
    
public:
	int getResult()
	{
    
    
		return m_Num1 * m_Num2;
	}
};


void test02()
{
    
    
	//创建加法计算器
	AbstractCalculator *abc = new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;  //用完了记得销毁

	//创建减法计算器
	abc = new SubCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;  

	//创建乘法计算器
	abc = new MulCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;
}

int main() {
    
    
	//test01();
	test02();
	system("pause");
	return 0;
}

Summary: C++ development advocates the use of polymorphism design program architecture, because polymorphism has many advantages

2.6.2. Pure virtual functions and abstract classes

  In polymorphism, usually the implementation of the virtual function in the parent class is meaningless, mainly calling the content rewritten by the subclass, so the virtual function can be changed to a pure virtual function . There is no need to write pure virtual functions when the parent class makes sense.
Pure virtual function syntax: virtual 返回值类型 函数名 (参数列表)= 0 ;
When there is a pure virtual function in a class, this class is also calledabstract class
Abstract classes are mainly used to implement interfaces and polymorphism, and can only be used in the form of pointers or references , that is, pointers or references to abstract classes can be created.
Abstract class features :

  • Could not instantiate object
  • The subclass must override the pure virtual function in the abstract class, otherwise it also belongs to the abstract class
class Base
{
    
    
public:
	//纯虚函数
	//类中只要有一个纯虚函数就称为抽象类
	//抽象类无法实例化对象
	//子类必须重写父类中的纯虚函数,否则也属于抽象类,无法实例化对象
	virtual void func() = 0; //纯虚函数,前面必须加virtual才可以等于0
};

class Son :public Base
{
    
    
public:
	//子类必须重写父类中的纯虚函数,否则也属于抽象类,无法实例化对象
	virtual void func() 
	{
    
    
		cout << "func调用" << endl;
	};
};

void test01()
{
    
    
	Base * base = NULL;
	// Base b;// 错误,抽象类无法实例化对象
	//base = new Base; // 错误,抽象类无法实例化对象。无论栈还是堆方式都不可以
	
	base = new Son;
	base->func();
	delete base;//记得销毁
}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

Polymorphic technology: the pointer of the parent class is used to refer to or point to the object of the subclass

Let's look at another example and make different drinks.

//抽象制作饮品
class AbstractDrinking {
    
    
public:
	//烧水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//倒入杯中
	virtual void PourInCup() = 0;
	//加入辅料
	virtual void PutSomething() = 0;
	//规定流程
	void MakeDrink() {
    
    
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};

//制作咖啡
class Coffee : public AbstractDrinking {
    
    
public:
	//烧水
	virtual void Boil() {
    
    
		cout << "煮农夫山泉!" << endl;
	}
	//冲泡
	virtual void Brew() {
    
    
		cout << "冲泡咖啡!" << endl;
	}
	//倒入杯中
	virtual void PourInCup() {
    
    
		cout << "将咖啡倒入杯中!" << endl;
	}
	//加入辅料
	virtual void PutSomething() {
    
    
		cout << "加入牛奶!" << endl;
	}
};

//制作茶水
class Tea : public AbstractDrinking {
    
    
public:
	//烧水
	virtual void Boil() {
    
    
		cout << "煮自来水!" << endl;
	}
	//冲泡
	virtual void Brew() {
    
    
		cout << "冲泡茶叶!" << endl;
	}
	//倒入杯中
	virtual void PourInCup() {
    
    
		cout << "将茶水倒入杯中!" << endl;
	}
	//加入辅料
	virtual void PutSomething() {
    
    
		cout << "加入枸杞!" << endl;
	}
};

//业务函数
void DoWork(AbstractDrinking* drink) {
    
    
	drink->MakeDrink();
	delete drink;
}

void test01() {
    
    
	DoWork(new Coffee);
	cout << "--------------" << endl;
	DoWork(new Tea);
}


int main() {
    
    
	test01();
	system("pause");
	return 0;
}

The core of polymorphism is to look for rewritten functions from subclasses through parent class pointers or references to similar objects

2.6.3. Virtual destructor and pure virtual destructor

  Let's introduce virtual destructor and pure virtual destructor. Why do we need to know this? We know that when polymorphism is used, the pointer or reference of the parent class is used to point to the subclass object, but the pointer of the parent class cannot release the destructor code in the subclass , so the data in the subclass is opened up to the heap area , then when the parent class pointer is there, deletethe data of the subclass cannot be released, which will cause a memory leak

When polymorphism is used, if there are attributes in the subclass that are allocated to the heap area, then the parent class pointer cannot call the destructor code of the subclass when it is released. Solution: Change the destructor function in the parent class to
virtual destructor or pure virtual destructor

Common features of virtual destructor and pure virtual destructor:

  • It can solve the problem that the parent class pointer cannot release the subclass object
  • need to have a specific function implementation

The difference between virtual destructor and pure virtual destructor:

  • If there is one in the code 纯虚析构, the class is an abstract class and objects cannot be instantiated. It does not conflict with the pure virtual function above, and the destructor is also a function.

Virtual destructor syntax:

virtual ~类名(){}

Pure virtual destructor syntax:

virtual ~类名() = 0;

类名::~类名(){}

class Animal {
    
    
public:

	Animal()
	{
    
    
		cout << "Animal 构造函数调用!" << endl;
	}
	virtual void Speak() = 0;

	//析构函数加上virtual关键字,变成虚析构函数
	//virtual ~Animal()
	//{
    
    
	//	cout << "Animal虚析构函数调用!" << endl;
	//}

	/*纯虚析构,利用纯虚析构和虚析构都可以解决,只能用一个。纯虚析构里面也要写上代码实现,不然直接写这句话报错,
	类外写上代码实现。区分跟纯虚函数的区别,纯虚函数可以直接写等于0*/
	//有了纯虚析构 之后 ,这个类也属于抽象类,无法实例化对象
	virtual ~Animal() = 0;
};
//如果这里不使用虚析构,将无法释放子类中的堆区数据
Animal::~Animal()
{
    
    
	cout << "Animal 纯虚析构函数调用!" << endl;
}

//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。

class Cat : public Animal {
    
    
public:
	Cat(string name)
	{
    
    
		cout << "Cat构造函数调用!" << endl;
		m_Name = new string(name);
	}
	virtual void Speak()
	{
    
    
		cout << *m_Name <<  "小猫在说话!" << endl;
	}
	~Cat()
	{
    
    
		cout << "Cat析构函数调用!" << endl;
		if (this->m_Name != NULL) {
    
    
			delete m_Name;
			m_Name = NULL;
		}
	}

public:
	string *m_Name;
};

void test01()
{
    
    
	Animal *animal = new Cat("Tom");
	animal->Speak();
	//父类指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
	//怎么解决?把基类改成一个虚析构函数
	//虚析构函数就是用来解决通过父类指针释放子类对象,是因为在运行时,会根据对象的实际类型调用相应的析构函数。这样就可以确保子类对象被正确地销毁,避免了内存泄漏和其他异常问题。
	delete animal;
}

int main() {
    
    

	test01();
	system("pause");
	return 0;
}

Summarize:

  1. Virtual destructor or pure virtual destructor is used to solve the problem of releasing subclass objects through parent class pointers. Both are acceptable. The difference is that after writing pure virtual destructor, this class belongs to an abstract class.
  2. If there is no heap area data in the subclass, it can not be written as virtual destructor or pure virtual destructor​
  3. A class with a pure virtual destructor is also an abstract class
  4. Both virtual and pure virtual destructors must havespecific letternumber realized.
  5. The pure virtual destructor in the parent class is also an abstract class, and the object cannot be instantiated.

2.7. Documentation

2.7.1. File operation

The data generated when the program is running is all temporary data, and will be released once the program finishes running.
C++'s operations on files are also based on object-oriented operations

Data can be persisted through files

File operations in C++ need to include header files< fstream >

There are two file types:

  1. Text files - files are stored on your computer as text in ASCII
  2. Binary files - files are stored on the computer in binary form of text, and users generally cannot read them directly

Three categories of operating files:

  1. ofstream: write operation
  2. ifstream: read operation
  3. fstream: read and write operations

2.7.2. Text files

The steps to write a file are as follows:

  1. Include header files
    #include <fstream>
  2. Create a stream object
    ofstream ofs;
  3. Open the file
    ofs.open("file path", open method);
  4. writedata
    ofs << "data written";
  5. Close the file
    ofs.close();

File open method:

open method explain
ios::in open file for reading
ios::out open file for writing
ios::ate Initial position: end of file
ios::app Appending to write files
ios::trunk If the file exists, delete it first, then create it
ios::binary binary mode

Note: The file opening method can be used in conjunction with the | operator

For example : write a file in binary modeios::binary | ios:: out

#include <fstream>

void test01()
{
    
    
	ofstream ofs;
	ofs.open("test.txt", ios::out);

	ofs << "姓名:张三" << endl;
	ofs << "性别:男" << endl;
	ofs << "年龄:18" << endl;

	ofs.close();
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

After the above program runs, a txt file will be generated at the location of the program
. Summary:

  • File operations must include the header file fstream
  • Read files can use ofstream, or fstream class
  • When opening a file, you need to specify the path of the operation file and the opening method
  • Use << to write data to the file
  • After the operation is complete, close the file

2.7.3. Reading files

The steps of reading a file are similar to those of writing a file, but there are more reading methods

The steps to read a file are as follows:

  1. Include header files
    #include <fstream>
  2. Create a stream object
    ifstream ifs;
  3. Open the file and judge whether the file is opened successfully
    ifs.open("file path", open method);
  4. Read data
    Four ways to read
  5. Close the file
    ifs.close();
#include <fstream>
#include <string>
void test01()
{
    
    
	ifstream ifs;
	ifs.open("test.txt", ios::in);

	if (!ifs.is_open())
	{
    
    
		cout << "文件打开失败" << endl;
		return;
	}

	//第一种方式
	//char buf[1024] = { 0 };
	//while (ifs >> buf)
	//{
    
    
	//	cout << buf << endl;
	//}

	//第二种
	//char buf[1024] = { 0 };
	//while (ifs.getline(buf,sizeof(buf)))
	//{
    
    
	//	cout << buf << endl;
	//}

	//第三种
	//string buf;
	//while (getline(ifs, buf))
	//{
    
    
	//	cout << buf << endl;
	//}

	char c;
	while ((c = ifs.get()) != EOF)
	{
    
    
		cout << c;
	}

	ifs.close();


}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

2.7.4. Binary file writing

Read and write files in binary mode

Open with to be specified asios::binary

2.7.5. Writing files

Writing files in binary mode mainly uses the stream object to call the member function write

Function prototype:ostream& write(const char * buffer,int len);

Parameter explanation: The character pointer buffer points to a storage space in memory. len is the number of bytes read and written

#include <fstream>
#include <string>

class Person
{
    
    
public:
	char m_Name[64];
	int m_Age;
};

//二进制文件  写文件
void test01()
{
    
    
	//1、包含头文件

	//2、创建输出流对象
	ofstream ofs("person.txt", ios::out | ios::binary);
	
	//3、打开文件
	//ofs.open("person.txt", ios::out | ios::binary);

	Person p = {
    
    "张三"  , 18};

	//4、写文件
	ofs.write((const char *)&p, sizeof(p));

	//5、关闭文件
	ofs.close();
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}
  • The file input stream object can read data in binary mode through the read function

2.7.6. Reading files

Reading files in binary mode mainly uses the stream object to call the member function read

Function prototype:istream& read(char *buffer,int len);

Parameter explanation: The character pointer buffer points to a storage space in memory. len is the number of bytes read and written

#include <fstream>
#include <string>

class Person
{
    
    
public:
	char m_Name[64];
	int m_Age;
};

void test01()
{
    
    
	ifstream ifs("person.txt", ios::in | ios::binary);
	if (!ifs.is_open())
	{
    
    
		cout << "文件打开失败" << endl;
	}

	Person p;
	ifs.read((char *)&p, sizeof(p));

	cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

Guess you like

Origin blog.csdn.net/qq_38683460/article/details/128235905