The most complete classes and objects in history, as long as you carefully read the C++ classes and objects, you will beat the interviewer every minute【C++】

Article directory

Basic

insert image description here
insert image description here

procedural and object oriented

The C language is process-oriented, focusing on the process, analyzing the steps to solve the problem, and solving the problem step by step through function calls.
C++ is based on object-oriented, focusing on objects, splitting one thing into different objects, and relying on the interaction between objects to complete.
For example, to create a food delivery system.
For process-oriented, we focus on the three processes of order placement, order receiving, and food delivery.
For object-oriented, we focus on the relationship between customers, merchants, and riders.

class introduction

Before C language, only variables can be defined in the structure, but in C++, not only variables can be defined in the structure, but also functions can be defined

struct Test
{
    
    
    //成员函数
	int Add(int x, int y)
	{
    
    
		return x + y;
	}
	//成员变量
	int a;
	double b;
	
};

But the definition of the above structure is preferred to use class instead in C++.

class Test
{
    
    
    //成员函数
	int Add(int x, int y)
	{
    
    
		return x + y;
	}
	//成员变量
	int a;
	double b;
	
};

#include<iostream>
//类域
struct Queue
{
    
    
	//成员函数 
	void Init()
	{
    
    

	}
};
struct Stack
{
    
    
	//成员函数 
	void Init(int defaultcapacity =4)
	{
    
    
		a = (int * )malloc(sizeof(int)*capacity);
		if (a == nullptr)
		{
    
    
			printf("malloc fail");
			exit(-1);
		}
		capacity = defaultcapacity;
		top = 0;
	}
	void Push(int x)
	{
    
    
		a[top++] = x;
	}
	void Destory()
	{
    
    
		free(a);
		a = nullptr;
		top = capacity;
	}
	//成员变量 
	//类域是一个整体 ,写在成员函数前面或者后面都行 
	int* a;
	int top;
	int capacity;
};
int main()
{
    
    
	struct Stack st1;
	st1.Init();
	Stack st2;//cpp的类
	st2.Init();
	st2.Push(1);
	st2.Push(2);
	st2.Push(3);
	st2.Push(4);
	st2.Destory();
	return 0;
}

class definition

class className
{
    
    
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号

class is the keyword to define the class, ClassName is the name of the class, and {} is the body of the class. Note that the semicolon after the end of the class definition cannot be omitted. The contents of the class body are called members of the class: variables in the class are called attributes or member variables of the class; functions in the class are called methods or member functions of the class

There are two ways to define a class:

  1. The declaration and definition are all placed in the class body. Note: If the member function is defined in the class, the compiler may treat it as an inline function.
class  Stack
{
    
    
public :
	//成员函数 
	//类里面定义的函数默认就是inline 
	//一般长的函数声明和定义分离,短的函数直接定义的类里面
	void Init(int defaultcapacity = 4)
	{
    
    
		a = (int*)malloc(sizeof(int) * capacity);
		if (a == nullptr)
		{
    
    
			printf("malloc fail");
			exit(-1);
		}
		capacity = defaultcapacity;
		top = 0;
	}
	void Push(int x)
	{
    
    
		a[top++] = x;
	}

	void Destory()
	{
    
    
		free(a);
		a = nullptr;
		top = capacity;
	}
private:
	//成员变量 
	//类域是一个整体 ,写在成员函数前面或者后面都行 
	int* a;
	int top;
	int capacity;


};
  1. The class declaration is placed in the .h file, and the member function definition is placed in the .cpp file. Note:The class name needs to be added before the member function name::

Func.h

#include<iostream>

using namespace std;

class  Stack
{
    
    

public:

	//成员函数 
	void Init(int defaultcapacity = 4);
	void Push(int x);
	void Destory();
private:
	//成员变量 
	//类域是一个整体 ,写在成员函数前面或者后面都行 
	int* a;
	int top;
	int capacity;
};

Func.cpp


#include"Func.h"

//指定类域
void Stack::Init(int defaultcapacity = 4)
{
    
    
	a = (int*)malloc(sizeof(int) * capacity);
	if (a == nullptr)
	{
    
    
		printf("malloc fail");
		exit(-1);
	}
	capacity = defaultcapacity;
	top = 0;
}

void Stack::Push(int x)
{
    
    
	a[top++] = x;
}

void Stack::Destory()
{
    
    
	free(a);
	a = nullptr;
	top = capacity;
}

In general, the second method is used.
Suggestions for member variable naming rules:

class Data
{
    
    
public :
	//成员函数
	void Init(int year)
	{
    
    
		_year = year;
	}
private:
	//成员变量 
	int _year;
	int _month;
	int day;
};

class access qualifier

The way of C++ to achieve encapsulation: use the class to combine the properties and methods of the object to make the object more perfect, and selectively provide its interface to external users through access rights

insert image description here
Access qualifier description:

  1. Publicly modified members can be directly accessed outside the class
  2. Protected and private modified members cannot be directly accessed outside the class (here protected and private are similar)
  3. Access scopes start at the occurrence of this access qualifier until the next occurrence of the access qualifier
  4. If there is no access qualifier behind, the scope will go to }, which is the end of the class.
  5. The default access permission of class is private, and struct is public (because struct is compatible with C)

Notice:Access qualifiers are only useful at compile time, when the data is mapped to memory, there is no difference in access qualifiers

What is the difference between struct and class in C++?

C++ needs to be compatible with C language, so struct in C++ can be used as a structure. In addition, struct in C++ can also be used to define classes. It is the same as class is to define a class, the difference is that the default access mode of members of struct is public, and the default access mode of members of class is private.

class encapsulation

Encapsulation: organically combine data and methods of manipulating data, hide object attributes and implementation details, and only expose interfaces to interact with objects

Encapsulation in the C++ language can organically combine data and methods of manipulating data through classes, hide internal implementation details of objects through access rights, and control which methods can be used directly outside the class, that is to sayWe use classes to encapsulate both data and methods. Those that do not want to be open to the outside world should be encapsulated with protected/private, and the members encapsulated with public allow the outside world to have reasonable access to them. So encapsulation is essentially a management

class scope

A class defines a new scope, and all members of the class are in the scope of the class. When defining members outside the class, you need to use the :: scope operator to indicate which class domain the member belongs to

Local and global domains affect the life cycle, class domains and namespace domains do not affect the life cycle

class instantiation

The process of creating an object with a class type is called instantiation of the class

1 A class describes an object. A class is just a model, which defines which members a class has. A class is defined without allocating actual memory space to store it.

Just like defining a structure in C language, when you have not created a variable with the custom type, the process of defining the structure type does not allocate actual memory space to store it.

2 A class can instantiate multiple objects, and the instantiated objects occupy the actual physical space and store class member variables

Just like a structure is defined in C language, and then a variable is created with this custom type, then this variable will occupy the actual physical space to store its member variables

3 Class instantiation of objects is like using architectural design drawings to build a house in reality. Classes are like design drawings. They only design what is needed, but there is no physical building. Similarly, a class is just a design. Instantiation The object can actually store data and occupy physical space

insert image description here

class object model

How to calculate the size of a class object

class A
{
    
    
public:
	void PrintA()
	{
    
    
		cout << _a << endl;
	}
private:
	char _a;
};

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

class Person
{
    
    
public:
	//显示基本信息
	void ShowInfo()
	{
    
    
		cout << _name << "-" << _sex << "-" << _age << endl;
	}
public:
	char* _name;  //姓名
	char* _sex;   //性别
	int _age;     //年龄
};

Guessing the storage method of class objects

The object contains the individual members of the class

insert image description here

Defect: The member variables in each object are different, but the same function is called. If stored in this way, when a class creates multiple objects, a code will be saved in each object, and the same code will be saved multiple times. Waste of space. So how to solve it?

Only one copy of the code is saved, and the address where the code is stored is saved in the object

insert image description here

Only member variables are saved, and member functions are stored in public code segments

For the above storage methods, which method is used by the computer to store, we can analyze the size of the following different objects:
insert image description here

Let's analyze it by obtaining the size of the different objects below

// 类中既有成员变量,又有成员函数
class A1 
{
    
    
public:
	//成员函数
	void f1() 
	{
    
    

	}
private:
	//成员变量 
	int _a;
};
// 类中仅有成员函数
class A2 
{
    
    
public:
	//成员函数 
	void f2() 
	{
    
    

	}
};
// 类中什么都没有---空类
//没有成员变量的类对象 ,需要1byte,是为了占位,表示对象存在 
//但是不存储有效数据
class A3
{
    
    

};
int main()
{
    
    
//对象中只存储了成员变量,没有存储成员函数 
	cout << sizeof(A1) << endl;//4
	cout << sizeof(A2) << endl;//1
	cout << sizeof(A3) << endl;//1

	return 0;
}

The size of these three objects is obtained through the unary operator sizeof. As a result, the size of A1 is 4 bytes, the size of A2 is 1 byte, and the size of A3 is also 1 byte.

in conclusion:The size of a class is actually the sum of "member variables" in the class. Of course, attention should be paid to memory alignment. Pay attention to the
size of the empty class. The empty class is special. The compiler gives the empty class a byte to uniquely identify the object of this class

If you are not clear about the memory alignment of the structure, please click here

this pointer

#include<iostream>
using namespace std;
class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//编译器对成员函数的处理 
	//void Print(Date* const this )
	//{
    
    
	//	cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	//}
private:
	int _year; // 年 声明 
	int _month; // 月
	int _day; // 日
	int a;
};
int main()
{
    
    
	Date d1, d2;
	d1.Init(2023, 5, 11);
	d2.Init(2023, 5, 11);
	d1.Print();
	//编译器的处理 
	//d1.Print(&d1);
	//d2.Print(&d2);

	d2.Print();
	return 0;
}

insert image description here

There are two member functions Init and Print in the Date class, and there is no distinction between different objects in the function body. Then when d1 calls the Init function, how does the function know that the d1 object should be set instead of the d2 object?
In C++, this problem is solved by introducing the this pointer, that is: the C++ compiler adds a hidden pointer parameter to each "non-static member function", so that the pointer points to the current object (the object that calls the function when the function is running), All "member variable" operations in the function body are accessed through this pointer. It's just that all operations are transparent to the user, that is, the user does not need to pass it, and the compiler completes it automatically

this cannot be explicitly passed between formal parameters and actual parameters, but it can be used explicitly inside the function

using namespace std;
class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << this << endl;
		cout << this->_year << "-" << _month << "-" << _day << endl;
	}
	//编译器对成员函数的处理 
	//void Print(Date* const this )
	//{
    
    
	//	cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	//}
private:
	int _year; // 年  声明
	int _month; // 月
	int _day; // 日
	int a;
};
int main()
{
    
    
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	//编译器的处理 
	//d1.Print(&d1);
	//d2.Print(&d2);

	d2.Print();
	return 0;
}

Characteristics of this pointer

  1. The type of this pointer: the type of the class * const, that is, in the member function,The this pointer cannot be assigned a value.

  2. Can only be used inside a "member function"

  3. The this pointer is essentially a formal parameter of a "member function". When an object calls a member function, the object address is passed as an actual parameter to this formal parameter. So the this pointer is not stored in the object.

Where does the this pointer exist?

this is a formal parameter, so the this pointer exists in the stack frame of the function call just like ordinary parameters.

  1. The this pointer is the first implicit pointer parameter of the "member function". Generally, it is automatically passed by the compiler through the ecx register and does not need to be passed by the user.

insert image description here
Under vS, the this pointer is passed and optimized. The object address is placed in ecx, and ecx stores the value of this pointer.

Can this pointer be null?

A deeper understanding of the this pointer through the following code


class A
{
    
    
public:

	void Print()//this指针是空的,但是函数内没有对this指针解引用
	{
    
    
		cout << "Print()" << endl;
	}
	//编译器对成员函数的处理 
	//	void Print(A * const this)//this指针是空的,但是函数内没有对this指针解引用
	//{
    
    
	//	cout << "Print()" << endl;
	//}
private:

	int _a;
};

int main()
{
    
    
	A* p = nullptr;
	p->Print();//并且p调用Print,不会发生解引用,因为Print的地址不在对象中,Print的地址在公共代码段。但是p会作为实参传递给this指针,p是一个nullptr ,传递一个nullptr并不会报错
	return 0;
}

The this pointer is empty, but the this pointer is not dereferenced in the function
and p calls Print, no dereference will occur, because the address of Print is not in the object, and the address of Print is in the public code segment. But p will be passed to this pointer as an actual parameter, p is a nullptr, passing a nullptr will not report an error.

class A
{
    
    
public:
	void PrintA()
	{
    
    
		cout << _a << endl;//this指针是空的,但是函数内访问_a,本质是this->_a
	}
	//编译器对成员函数的处理 
	//	void PrintA(A * const this )
	//{
    
    
	//	cout << this->_a << endl;//this指针是空的,但是函数内访问_a,本质是this->_a
	//}
private:
	int _a;
};
int main()
{
    
    
	A* p = nullptr;
	p->PrintA();
	return 0;
}

The this pointer is empty, but accessing _a within the function is essentially this->_a
and p calls Print, no dereferencing will occur, because the address of Print is not in the object. p will be passed as an actual parameter to the this pointer.
insert image description here

Improve articles

insert image description here

The 6 default member functions of the class

If a class has no members, we simply call it an empty class. But is there really nothing in the empty class? In fact, in any class, even if we don't write anything, 6 default member functions will be automatically generated in the class.

class Date 
{
    
    

}; //空类

insert image description here

Constructor

Constructor: The name is the same as the class name, and is automatically called by the compiler when creating a class type object, ensuring that each data member has an appropriate initial value, and is called only once during the life cycle of the object.

For example, the member function Date in the following Date class is a constructor. When you create an object with the date class, the compiler will automatically call the constructor to initialize the newly created variable

characteristic

The constructor is a special member function. It should be noted that although the name of the constructor is called construction, the main task of the constructor is not to open space to create objects, but to initialize objects

Its characteristics are as follows:

  1. The constructor function name is the same as the class name.

  2. The constructor has no return value.
    No return value does not mean that the return value is void, but the function has no return value

  3. The compiler automatically calls the corresponding constructor when the object is instantiated.

class Stack
{
    
    
public:
	//构造函数 :初始化 
	Stack(  int capacity =4 )
	{
    
    
		printf("Stack(  int capacity =4 )");
		_array = (int*)malloc(sizeof(int) * capacity);
		if (_array == NULL)
		{
    
    
			printf("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;

	}
	void Push(int x)
	{
    
    
		_array[_size++] = x;
	}
	//析构函数 : 完成对象中资源的清理工作
	~Stack( )
	{
    
    
		printf("~Stack( )");
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_size = 0;
			_capacity = 0;
		}
   }

private:
	int* _array;
	int _capacity;
	int _size;

};
int main()
{
    
    
	Stack s;
	s.Push(1);
	s.Push(2);
	s.Push(3);

	return 0;
}
  1. Constructors can be overloaded

Constructor supports function overloading

class Stack
{
    
    
public:
	//函数重载 
	Stack(int* a, int n)
	{
    
    
		cout << "Stack(int* a, int n)" << endl;
		_array = (int*)malloc(sizeof(int) * n);
		if (_array == NULL)
		{
    
    
			printf("malloc fail");
			exit(-1);
		}
		memcpy(_array, a, sizeof(int) * n);
		_capacity = n;
		_size = n;

	}
	//构造函数 :初始化 
	Stack(int capacity = 4)
	{
    
    
		printf("Stack(  int capacity =4 )");
		_array = (int*)malloc(sizeof(int) * capacity);
		if (_array == NULL)
		{
    
    
			printf("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;

	}
	void Push(int x)
	{
    
    
		_array[_size++] = x;
	}
	//析构函数 : 完成对象中资源的清理工作
	~Stack()
	{
    
    
		printf("~Stack( )");
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_size = 0;
			_capacity = 0;
		}
	}

private:
	int* _array;
	int _capacity;
	int _size;

};
int main()
{
    
    
	Stack s;
	s.Push(1);
	s.Push(2);
	s.Push(3);

	return 0;
}

5 Regarding the destructor automatically generated by the compiler, will it accomplish something? In the following program, we will see that the default destructor generated by the compiler calls its destructor for the custom type member

class Date
{
    
    
public:
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	Date d2;
	d1.Print();
	return 0;
}

insert image description here

Supplement: Built-in types/basic types (basic types defined by the language itself) such as
custom types such as int/char/double/pointers of any type , types defined with struct/class, etc.

If we don't write a constructor, the compiler will generate a constructor by default,The built-in type does not handle it (some compilers will handle it, that is the personalized behavior of the compiler, and C++ does not handle it), and the custom type will call its default constructor

In general, if there are built-in type members, you need to write the constructor yourself, and you cannot use the compiler to write it yourself, because most compilers do not handle built-in types

If all are custom type members, you can consider letting the compiler generate it by itself


When the C++11 standard was released, a patch was applied, and the default value can be given to the member declaration

class Stack
{
    
    

};
class Date
{
    
    
public:
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	//内置类型
	//c++11支持 ,这里不是初始化 ,这里是声明,并没有开空间 
	//这里是给的默认缺省值,是给编译器生成的默认构造函数用的
	int _year = 1;
	int _month = 1;
	int _day = 1;
	//自定义类型 
	//Stack _st;
};
int main()
{
    
    
	Date d1;
	Date d2;
	d1.Print();
	return 0;
}
  1. If no constructor is explicitly defined in the class, the C++ compiler will automatically generate a parameterless default constructor, once the user explicitly defines the compiler will no longer generate

The constructor mechanism automatically generated by the compiler:
 1. The constructor automatically generated by the compiler does not deal with built-in types.
 2. For custom types, the compiler will call their own default constructors.

Although the compiler will automatically generate the constructor if we don't write it, the constructor automatically generated by the compiler may not achieve the effect we want, so in most cases we need to write the constructor ourselves

constructor call

class Date
{
    
    
public:
	//构造函数支持函数重载 
	Date()
	{
    
    
		_year = 1;
	   _month = 1;
	   _day = 1;
	}
	 Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	//内置类型
	//c++11支持 ,这里不是初始化 ,这里是声明,并没有开空间 
	//这里是给的默认缺省值,是给编译器生成的默认构造函数用的
	int _year = 1;
	int _month = 1;
	int _day = 1;
	//自定义类型 
	//Stack _st;
};


int main()
{
    
    

	//构造函数的调用跟普通函数也不一样
	Date d1;
	//Date d1(); ,是错的
	Date d2(2023,5,12);
	Date d3(2023);
	d2.Print();
	d3.Print();

	return 0;
}

default constructor

The following three types are called default constructors:
1. We don't write the constructors that are automatically generated by the compiler.
 2. A parameterless constructor written by ourselves.
 3. The default constructor written by ourselves.
All in all, the constructor that can be called without passing parameters is the default constructor

No-argument constructor and full default constructor, we did not write the default constructor generated by the compiler, which can be considered as the default constructor,But these three have and can only appear one
That is to say, the default constructor can be called without passing parameters.

What happens if both a no-argument constructor and a full-default constructor exist at the same time?

The constructor gives the full default value, and the constructor does not pass parameters. Putting it together, although it satisfies function overloading, there will be ambiguity when calling without parameters. Generally, you choose to use the constructor to give the full default value.

insert image description here

destructor

Contrary to the function of the constructor, the destructor is responsible for completing the destruction of the object. When the object is destroyed, the destructor will be called automatically to complete some resource cleaning work of the class

Characteristics of the destructor

1 The function name of the destructor is to add the character '~' before the class name

class Date
{
    
    
public:
	Date()// 构造函数
	{
    
    
	
	}
	~Date()// 析构函数
	{
    
    
	
	}
private:
	int _year;
	int _month;
	int _day;
};

2 The destructor has no parameters and no return value

No return value does not mean that the return value is void, but the function has no return value

3 A class can only have one destructor. If not explicitly defined, the system will automatically generate a default destructor. Note: Destructors cannot be overloaded

The destructor mechanism automatically generated by the compiler:

1. The destructor automatically generated by the compiler does not deal with built-in types.
2. For custom types, the compiler will call their own default destructors.

4 When the life cycle of the object ends, the C++ compiler will automatically call the destructor

In the past, the destruction of C language was often forgotten, which would lead to memory leaks. Now with the destructor, the problem of forgetting to release space in C language is greatly reduced
.has one and only one destructor. If no definition is displayed, the system will automatically generate a default destructor

6. The ones constructed first are then destructed, and the ones constructed later are destructed first

The object is defined in the function, and the function call will create a stack frame, and the object construction and destruction in the stack frame should also conform to the principle of first in, last out

insert image description here

**Summary:
1. In general, if there is a dynamic application for resources, it is necessary to explicitly write the destructor to release the resources


2. There is no resource for dynamic application, no need to write destructor


3. The members that need to release resources are all self-defined types, and there is no need to write destructors


copy constructor

Copy constructor: There is only a single formal parameter, which is a reference to the object of this class type (usually const decoration is commonly used), and it is used inCalled automatically by the compiler when an existing class type object creates a new object

Traits of the copy constructor

1. The copy constructor is an overloaded form of the constructor

Because the function name of the copy constructor is also the same as the class name.

2. The copy constructor has only one parameter and must be passed by reference. Using the value-by-value method will cause infinite recursive calls

First look at a piece of code:

#include<iostream>
using namespace std;

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2023 ,int month = 5,int day =12)
	 {
    
    
		_year = year;
		_month = month;
		_day = day;
	 }
	//拷贝构造函数
    Date ( Date d)//err
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;

	}
private:
	int _year;
	int _month;
	int _day;

};

int main()
{
    
    
	Date d1;
	Date d2(d1);//用已存在的对象d1创建对象d2

	return 0;
}

To call the copy constructor, you need to pass the parameters first. If the parameters are passed by value, then the copy construction of the object needs to be carried out in the process of passing the parameters, and so on, which will eventually lead to infinite recursive calls.insert image description here

C++ stipulates:
built-in types are directly copied,
and custom types must call copy construction to complete the copy

To call the copy constructor, you need to pass the parameters first. If the parameters are passed by value, then the copy construction of the object needs to be carried out in the process of passing the parameters, and so on, which will eventually lead to infinite recursive calls.
insert image description here
In order to solve the above problems, we adopt the form of reference, which does not open up space from a grammatical point of view, and d is an alias of d1

#include<iostream>
using namespace std;

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2023 ,int month = 5,int day =12)
	 {
    
    
		_year = year;
		_month = month;
		_day = day;
	 }
	//拷贝构造函数
    Date ( const Date & d)//引用
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;

	}
private:
	int _year;
	int _month;
	int _day;

};

int main()
{
    
    
	Date d1;
	Date d2(d1);//用已存在的对象d1创建对象d2

	return 0;
}

Built-in type copy and custom type copy

built-in type copy

void func(int i)
{
    
    

}
void func(Date d)
{
    
    

}
int main()
{
    
    
	func(10);//C++内置类型直接拷贝
	func(d1);//C++内置类型直接拷贝
	return 0;
}

The custom type calls the copy constructor to complete the copy

#include<iostream>
using namespace std;

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2023 ,int month = 5,int day =12)
	 {
    
    
		_year = year;
		_month = month;
		_day = day;
	 }
	//拷贝构造函数
    Date (const Date &d)
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;

	}
private:
	int _year;
	int _month;
	int _day;

};

int main()
{
    
    
	Date d1;
	Date d2(d1);//用已存在的对象d1创建对象d2

	return 0;
}

3. If not explicitly defined, the compiler will generate a default copy constructor. The default copy constructor object is copied in byte order according to memory storage. This kind of copy is called shallow copy, or value copy

The copy constructor mechanism automatically generated by the compiler:
 1. The copy constructor automatically generated by the compiler will perform a shallow copy (value copy) for built-in types.
 2. For custom types, the compiler will call their own default copy constructor function

#include<iostream>
using namespace std;

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2023 ,int month = 5,int day =12)
	 {
    
    
		_year = year;
		_month = month;
		_day = day;
	 }
private:
	int _year;
	int _month;
	int _day;

};

int main()
{
    
    
	Date d1(2023,5,12);
	Date d2(d1);//用已存在的对象d1创建对象d2,我们不写,编译器会生成默认的拷贝构造函数
	return 0;
}

insert image description here

In the above code, we did not write the copy constructor ourselves, but the copy constructor automatically generated by the compiler still completes the copy construction of the object

So here is a question?
Is not all of us can not write the copy constructor, use the copy constructor automatically generated by the compiler
Please see the following code

class Stack
{
    
    
public:
   //构造函数 
	Stack(int capacity = 4)
	{
    
    
		cout << "Stack()" << endl;

		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}

		_capacity = capacity;
		_top = 0;
	}

	// st2(st1) ,拷贝构造函数 
	Stack(const Stack& st)
	{
    
    
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (nullptr == _a)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}

		memcpy(_a, st._a, sizeof(int) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}
	//析构函数 
	~Stack()
	{
    
    
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}

private:
	int* _a = nullptr;
	int _top = 0;
	int _capacity;
};

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
    
    
		cout << "Date(Date& d)" << endl;
		this->_year = d._year;
		_month = d._month;
		_day = d._day;
	
	}
private:
	int _year;
	int _month;
	int _day;
};

class MyQueue
{
    
    
private:
    //自定义类型
	Stack _pushst;
	Stack _popst;
};
int main()
{
    
    


	// 可以不写,默认生成的拷贝构造就可以用
	Date d1(2023, 5, 12);
	Date d2(d1);


	// 必须自己实现,实现深拷贝
	Stack st1;
	Stack st2(st1);

	return 0;
}

insert image description here
The problem with the above code is that the copy constructor is not written, and the copy constructor automatically generated by the compiler, _a of st1 and _a of st2 in the above figure point to the same space, the compiler calls the destructor, and calls 2 times, the space pointed to by _a has been destroyed by calling the destructor for the first time, and the illegal access of memory is caused by calling the destructor for the second time

Summarize:

The Date function does not need to write a copy constructor, because the copy constructor automatically generated by the compiler will complete a shallow copy (value copy) of the built-in type. The MyQueue function does not need to write a
copy constructor, because for a custom type, the compiler will To call their own default copy constructor
For classes like Stack, shallow copy will cause problems such as double destruction and program crash, so we need to write the corresponding copy constructor by ourselves

4. The copy constructor automatically generated by the compiler cannot implement deep copy

assignment operator overloading

In order to enhance the readability of the code, C++ introduces operator overloading. Operator overloading is a function with a special function name. Its purpose is to allow custom types to be directly operated by operators like built-in types .

The function name is: the keyword operator followed by the operator symbol that needs to be overloaded.

Function prototype: return value type operator operator (parameter list)

Notice:

1. You cannot create a new operator by connecting other symbols: such as operator@
2. An overloaded operator must have a class type parameter
3. The meaning of an operator used for a built-in type cannot be changed, for example: a built-in integer +, cannot change its meaning
4. When overloaded as a class member function, its formal parameters seem to be 1 less than the number of operands, because the first parameter of the member function is the hidden this

5. .* :: sizeof ?: . Note that the above 5 operators cannot be overloaded. This often appears in written multiple choice questions

for example:

class Date
{
    
    
public :
	//构造函数 
	Date(int year =2023,int month =5,int day =13)
	{
    
    
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	//拷贝构造 
	//用已存在的对象d1创建对象d2 
	Date( const Date & d)
	{
    
    
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
//private:
	int _year;
	int _month;
	int _day;
};
bool operator< (const Date & x1, const Date & x2)
{
    
    
	if (x1._year < x2._year)
	{
    
    
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
    
    
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._month < x2._day)
	{
    
    
		return true;
	}
	else
	{
    
    
		return false;
	}
}
int main()
{
    
    
	Date d1(2023, 5, 12);
	Date d2(2023, 2, 12);
	cout << (d1 < d2) << endl;
	cout << ( operator< (d1,d2)) << endl;
	return 0;
}

We put the operator overloading function outside the class, but there will be a problem at this time: the member variables in the class cannot be accessed by the outside, so we set the member variables in the class from private to public, so that the outside can access the class. The member variable is gone
and there is no this pointer outside the class, so at this time the formal parameters of the function must be explicitly set to two.

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2023, int month = 5, int day = 13)
	{
    
    
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	//拷贝构造 
	//用已存在的对象d1创建对象d2 
	Date(const Date& d)
	{
    
    
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	bool operator< (const Date& x)
	{
    
    
		if (_year < x._year)
		{
    
    
			return true;
		}
		else if (_year == x._year && _month < x._month)
		{
    
    
			return true;
		}
		else if (_year == x._year && _month == x._month && _month < x._day)
		{
    
    
			return true;
		}
		else
		{
    
    
			return false;
		}
	}
//	bool operator< (Date* const  this ,const Date& x)
//此时该函数的第一个形参默认为this指针。
	//{
    
    
	//	if (this->_year < x._year)
	//	{
    
    
	//		return true;
	//	}
	//	else if (this->_year == x._year && this->_month < x._month)
	//	{
    
    
	//		return true;
	//	}
	//	else if (this->_year == x._year && this->_month == x._month && this->_month < x._day)
	//	{
    
    
	//		return true;
	//	}
	//	else
	//	{
    
    
	//		return false;
	//	}
	//}
	private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1(2023, 5, 12);
	Date d2(2023, 2, 12);//是否要重载运算符,取决于运算符对这个类是否有意义
	d1 < d2;	//转换成d1.operator<(d2);

	return 0;
}

insert image description here


From the perspective of assembly, d1 < d2 is the same as d1.operator<(d2), but we usually write d1<d2

We use the operator overload function as a member function of the class. At this time, the formal parameters of the function are still set to two, but the first formal parameter of the function is the this pointer by default.

Summarize:

The operator has several operands, and the overloaded function has several parameters

operator overloading

Operator overloaded functions also have their own return type, function name, and parameter list. Its return value type and parameter list are similar to ordinary functions. Operator overloading function name: the keyword operator is followed by the operator symbol that needs to be overloaded

Summary:
Whether to overload the operator depends on whether the operator makes sense for this class

assignment operator overloading

Here is an example of overloading the = operator:

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2021, int month = 1, int day = 4)
	{
    
    
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	//拷贝构造 
	//用已存在的对象d1创建对象d2 
	Date(const Date& d)
	{
    
    
		cout<< "	Date(const Date& d)" << endl;
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	//赋值运算符重载
	//d4 = d1
	// Date & operator= (Date* const this,  const Date & d ) 
   Date & operator= (const Date & d ) //出了对象this生命周期还在 
	{
    
    
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;

		return *this;  // 返回d4,this就是d4的地址 ,所以返回*this
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1(2023, 5, 12);
	Date d2(2022, 2, 12);//是否要重载运算符,取决于运算符对这个类是否有意义
	d1 = d2;//已经存在的两个对象之间的复制拷贝 (运算符重载函数)

	/*Date d2(d1);*///用一个已经存在的对象初始化另一个对象(本质是一个构造函数 )
	Date d5, d4;
	//d4 =d1 其实就是 d4.operator=(d1)
	d5 = d4 = d1;
	return 0;
}

1. There are a few points to note when overloading assignment operators:

1. Parameter type: const T&, passing by reference can improve the efficiency of parameter passing

The first formal parameter of the assignment operator overloaded function defaults to the this pointer, and the second formal parameter is the right operand of our assignment operator.
 Since it is a custom type of parameter passing, if you use value passing, the copy constructor will be called one more time, so the second parameter of the function is best to use reference passing (the first parameter is the default this pointer, we can't control it ).
 Secondly, the second parameter, that is, the right operand of the assignment operator, will not modify the second parameter in the function body, so it is best to add const

2. Return value type: T&, return reference can improve the efficiency of return, and the purpose of return value is to support continuous assignment

In fact, if we only use the assignment operator in the way of d2 = d1, the assignment operator overload function does not need to have a return value, because d2 has been modified through the this pointer in the function body. But in order to support continuous assignment, that is, d3 = d2 = d1, we need to set a return value for the function, and obviously,The return value should be the left operand of the assignment operator, which is the object pointed to by the this pointer.

Just like passing parameters by reference, passing parameters by reference is to avoid unnecessary copying. If the function scope is out at this time, the object pointed to by this pointer has not been destroyed. At this time, it is best to return by reference to reduce unnecessary copying ,Improve efficiency

3. Check whether you assign a value to yourself

If d1 = d1 appears, there is no need to perform an assignment operation, so before performing an assignment operation, it is best to judge whether it is to assign a value to yourself

4. Return *this: to compound the meaning of continuous assignment

When the assignment operation is completed, we should return the left operand of the assignment operator, but in the function body we can only access the left operand through the this pointer, so to return the left operand, we can only return *this

Distinguish between assignment operator overloaded functions and copy constructors

Seeing this, I believe that you have a clearer understanding of copy constructors and assignment operator overloading functions,
but pay attention to the distinction: copy constructors and assignment operator overloading functions

insert image description here

Copy constructor: Use an existing object to construct and initialize another object to be created.
Assignment operator overloading function: assigns one object to another object when both objects already exist.

class Date
{
    
    
public:
	//构造函数 
	Date(int year = 2021, int month = 1, int day = 4)
	{
    
    
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	//拷贝构造 
	//用已存在的对象d1创建对象d2 
	Date(const Date& d)
	{
    
    
		cout << "	Date(const Date& d)" << endl;
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	//赋值运算符重载
	//d4 = d1
	Date& operator= (const Date& d) //出了对象this生命周期还在 
	{
    
    
		//d1 =d1 
		if (this != &d)
		{
    
    
			this->_year = d._year;
			this->_month = d._month;
			this->_day = d._day;
	  }
		 
		return *this;  //返回*this 这个对象的别名
	}


	//Date& operator= (Date * const  this ,const Date& d) //出了对象this生命周期还在 
	//{
    
    
	//	//d1 =d1 
	//	if (this != &d) //比较的是地址 ,&d是取地址,防止自己给自己赋值
	//	{
    
    
	//		this->_year = d._year;
	//		this->_month = d._month;
	//		this->_day = d._day;
	//	}

	//	return *this;  //返回*this 这个对象的别名
	//}
private:
	int _year;
	int _month;
	int _day;
};

	int main()	
	{
    
    
	d1 = d2;//已经存在的两个对象之间的复制拷贝 (运算符重载函数)
	Date d2(d1);//用一个已经存在的对象d1初始化另一个对象d2(本质是一个构造函数 )
	Date d3 = d1;//调用的也是拷贝构造函数
	return 0 ;
	}	

2. The assignment operator can only be overloaded as a member function of a class and cannot be overloaded as a global function.
Look at the following code:

class Date
 
{
    
    
 
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
 
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
 
Date& operator=(Date& left, const Date& right)
{
    
    
	if (&left != &right)
	{
    
    
		left._year = right._year;
		left._month = right._month;
		left._day = right._day;
	}
	return left;
}

Reason: If the assignment operator is not explicitly implemented, the compiler will generate a default one. At this time, if the user implements a global assignment operator overload outside the class, it will conflict with the default assignment operator overload generated by the compiler in the class, so the assignment operator overload can only be a member function of the class.
insert image description here

3. When the user does not explicitly implement, the compiler will generate a default assignment operator overload, which is copied byte-by-byte by value

Notice:Built-in type member variables are directly assigned, while custom type member variables need to call the assignment operator overload of the corresponding class to complete the assignment

That is to say, the behavior of assignment overload generated by default is the same as that of copy construction:
1. The assignment overload function automatically generated by the compiler will perform a shallow copy (value copy) for built-in types.
2. For custom types, the compiler will call them again Own assignment overload function

class Time
{
    
    
public:
	Time()
	{
    
    
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{
    
    
		if (this != &t)
		{
    
    
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
 
class Date
 
{
    
    
 
private:
	// 基本类型(内置类型)
 
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
 
	Time _t;
};
 
int main()
{
    
    
	Date d1;
	Date d2;
	d1 = d2;
	return 0;
}

Since the default assignment operator overload function generated by the compiler can already complete byte-ordered value copying, there is no need to implement it yourself, such as the date class.
If the following class is not written, to use the default assignment operator overload function generated by the compiler?

Here you will find that the following program will crash, and here we need the deep copy we will learn later to solve.

typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 10)
	{
    
    
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
 
private:
	DataType *_array;
	size_t _size;
	size_t _capacity;
};
 
int main()
{
    
    
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2;
	s2 = s1;
	return 0;
}

If resource management is not involved in the class, the assignment operator can be implemented or not; once resource management is involved, it must be implemented
insert image description here

Summarize:

The Date function does not need to write an assignment overload function, because the assignment overload function automatically generated by the compiler will complete a shallow copy (value copy) of the built-in type. The MyQueue function does not need to write an assignment overload function,
because for a custom type, the compiler The handlers will call their own default assignment overloading functions.
For classes like Stack, shallow copying will cause problems such as destructuring twice and program crashes. We need to write the corresponding assignment overloading functions ourselves.
insert image description here

const member

Member functions of const modified classes

We call const-decorated class member functions const member functions, const-modified class member functions actually modify the implicit this pointer of class member functions, indicating that the object pointed to by this pointer cannot be modified in this member function.

insert image description here

For example, we can const-modify the printing function in the class member function to avoid accidentally modifying the object in the function body:

class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
	void Print()
	{
    
    
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
			
	}
	void Print() const
	{
    
    
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1(2023, 5, 5);
	d1.Print(); //d1.Print(&d1)

	const Date d2(2023, 5, 5);
	d2.Print();//d2.Print(&d2)

	return 0;
}

insert image description here

Why add const after void Print() instead of adding const in the brackets, because the this pointer cannot be displayed and written, const can only be added after, adding const is equivalent to const Date* const this, and the added const is modified by * this

After adding const to the member function, both ordinary objects and const objects can be called, the permissions of ordinary objects are reduced, and the permissions of const objects are shifted

Can all member functions add this const?
No,The function of the object member variable to be modified cannot be added

Conclusion: So as long as the member variable is not modified inside the member function, const should be added
so that both const objects and ordinary objects can be called

Think about the following questions (classic interview questions):
1. Can a const object call a non-const member function?
2. Can a non-const object call a const member function?
3. Can other non-const member functions be called within a const member function?
4. Can other cosnt member functions be called in non-cosnt member functions?

insert image description here

The answer is: no, yes, no, yes
The explanation is as follows:
 1. Non-const member function, that is, the this pointer of the member function is not modified by const, we pass in an object modified by const, and use the object not modified by const This pointer is received, which belongs to the amplification of permissions, and the function call fails.
 2. const member function, that is, the this pointer of the member function is modified by const, we pass in an object that is not modified by const, and receive it with the this pointer modified by const, which belongs to the reduction of permissions, and the function call is successful.
 3. Call other member functions not modified by const in a member function modified by const, that is, assign the value of this pointer modified by const to a this pointer not modified by const, which belongs to the amplification of authority , the function call failed.
 4. Calling other member functions modified by const in a member function not modified by const, that is, assigning the value of this pointer not modified by const to a this pointer modified by const, which belongs to the reduction of permissions , the function call succeeds.

const int a =10  ;
int b = a ;
const int a =10  ;
int& b = a ;

insert image description here

Implementation of the date class

After learning the 6 default member functions of C++, we implement a complete date class,
 which is the member functions and member variables contained in the date class:

class Date
{
    
    
public:
	// 构造函数
	Date(int year = 0, int month = 1, int day = 1);
	// 打印函数
	void Print() const;
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day) const;
	// 日期-=天数
	Date& operator-=(int day);
	// 日期-天数
	Date operator-(int day) const;
	// 前置++
	Date& operator++();
	// 后置++
	Date operator++(int);
	// 前置--
	Date& operator--();
	// 后置--
	Date operator--(int);
	// 日期的大小关系比较
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;
	// 日期-日期
	int operator-(const Date& d) const;

	// 析构,拷贝构造,赋值重载可以不写,使用默认生成的即可

private:
	int _year;
	int _month;
	int _day;
};

Date class constructor

// 获取某年某月的天数
inline int GetMonthDay(int year, int month)
{
    
    
	// 数组存储平年每个月的天数
	static int dayArray[13] = {
    
     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	int day = dayArray[month];
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
    
    
		//闰年2月的天数
		day = 29;
	}
	return day;
}
// 构造函数
Date::Date(int year, int month, int day)
{
    
    
	// 检查日期的合法性
	if (year >= 0
	&& month >= 1 && month <= 12
	&& day >= 1 && day <= GetMonthDay(year, month))
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
    
    
		// 严格来说抛异常更好
		cout << "非法日期" << endl;
		cout << year << "年" << month << "月" << day << "日" << endl;
	}
}

There are three points to note about the GetMonthDay function:
 1. The GetMonthDay function will be called multiple times, so it is set as an inline function, and there is no need to create a function stack frame to improve code efficiency 2.
 To prevent the need to re-open the array every time the GetMonthDay function is called, create The new stack frame, the dayArray array is modified with static and stored in the static area
 3. Judgment of leap year: leap every four years, no leap for a hundred years, leap again for four hundred years

int Date::GetMonthDay(int year, int month)
{
    
    
	static int daysArr[13] = {
    
     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	//是闰年 
	if  (month == 2 && (  (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) )) 
	{
    
    
		//四年一闰,百年不闰,四百年再闰
		return 29;
	}
	else
	{
    
    
		return daysArr[month];
	}
}

Print function of date class

// 打印函数
void Date::Print() const
{
    
    
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

Comparison of the size relationship of the date class

It seems that there are 6 operators that need to be overloaded for the comparison of the size relationship of the date class. In fact, we only need to implement < and ==, and then the rest can be realized by reusing these two operators.

Overloading of the < operator

bool Date::operator< (const Date& x)
{
    
    
	//bool Date::operator ( Date * const this , const Date &x )
	if (this->_year < x._year)
	{
    
    
		return true;
	}
	else if (this->_year == x._year && this->_month < x._month)
	{
    
    
		return true;
	}
	else if (this->_year == x._year && this->_month == x._month && this->_day < x._day)
	{
    
    
		return true;
	}

	return false;
}

Overloading of the == operator


bool Date:: operator== (const Date& x)
{
    
    
	return this->_year == x._year
		&&	this->_month == x._month
		&&  this->_day == x._day;
}

Overloading of the >= operator

date += number of days

The overloaded function of the += operator uses a reference return, because the object pointed to by the this pointer is not destroyed when the function is out of scope

// 日期+=天数
Date& Date::operator+= (int day)
//Date Date::operator+ (Date *  const this ,int day)
{
    
    
	this->_day += day;
    
	while (   this->_day   >     GetMonthDay( this->_year,  this->_month)  )
	{
    
    
		this->_day -= GetMonthDay(this->_year, this->_month);
		this->_month++;
		if (this->_month == 13)
		{
    
    
			this->_year++;
			this->_month = 1;
		}
	}
	return *this;
}

Overloading of the <= operator

bool Date::operator<= (const Date& x)
{
    
    
	//复用之前的函数 
	return  *this < x  ||  *this ==x ;
}

Overloading of the != operator

bool Date::operator!=  (const Date& x)
{
    
    
	return !(*this == x);
}

> operator overloading

bool Date::operator> (const Date& x)
//bool Date::operator> ( Date *this , const Date &x )
//d1 >d2 ,d1 是*this ,d2 是x
{
    
    
	return !(*this<=x);
}
bool Date::operator>=  (const Date& x)
{
    
    
	return !(*this < x);
}
bool Date::operator!=  (const Date& x)
{
    
    
	return !(*this == x);
}
在这里插入代码片

date + number of days

The overload of the + operator reuses the overloaded function of the previous += operator.The post ++ returns the value before adding, but the value of the object itself has not changed. Just like a = b + 1, the return value of b + 1 is b + 1, but the value of b is unchanged. The return value of the overloaded function of the + operator can only be returned by value, because the object tmp is destroyed when it goes out of the scope of the function, and cannot be returned by reference

Date Date::operator+ (int day)//后置++
//Date Date::operator+ (Date* const this ,int day  )
{
    
    
	Date tmp(*this);//用已存在的对象*this拷贝创建tmp 

	//tmp._day += day;

	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	//{
    
    
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
	//	tmp._month++;
	//	if (tmp._month == 13)
	//	{
    
    
	//		tmp._year++;
	//		tmp._month = 1;
	//	}
	//}

	//复用复用operator+=
	tmp += day;
	return tmp;
}

prefix ++
Both pre-++ and post-++ are ++, pre-++ returns the object after ++, and post-++ returns the object before ++, there is almost no difference between pre-++ and post-++ for built-in types, but for custom types, the difference between pre-++ and post-++ will be very large, and pre-++ is preferred for custom types
In order to distinguish the operator overloading of pre-++ and post-++, we add an int parameter to the parameter of operator overloading of post-++. When using post-++, it is not necessary to pass in an actual parameter to this int parameter. Adding this int parameter is not for a specific value, it is just a placeholder, and the preposition ++ constitutes a function overload

//前置++
Date& Date::operator++()
{
    
    
	
	*this += 1;
	return *this;
}

date += number of days

Date& Date::operator+= (int day)
//Date Date::operator+ (Date *  const this ,int day)
{
    
    
	if (day < 0)
	{
    
    
		return *this -= -day;
	}
	this->_day += day;
    
	while (   this->_day   >     GetMonthDay( this->_year,  this->_month)  )
	{
    
    
		this->_day -= GetMonthDay(this->_year, this->_month);
		this->_month++;
		if (this->_month == 13)
		{
    
    
			this->_year++;
			this->_month = 1;
		}
	}
	return *this;
}

date -= number of days

Date Date::operator-= (int day)
//Date Date::operator( Date * const this  ,int day )
{
    
    
	this->_day -= day;
    
	while (this->_day <=0)
	{
    
    
		--this->_month;
		if (this->_month == 0)
		{
    
    
			this->_month = 12;
			--this->_year;
		}
		this->_day += GetMonthDay(this->_year ,this->_month);
	}
	return *this ;
}


date - number of days

Similar to the overloading of the + operator, the above -= operator overloading function is reused, and the tmp object is destroyed out of the function scope, so it cannot be returned by reference.

// 日期-天数
Date Date::operator-(int day) const
{
    
    
	Date tmp(*this);// 拷贝构造tmp,用于返回
	// 复用operator-=
	tmp -= day;

	return tmp;
}

Date.h

#include<iostream>
using namespace std;

class Date
{
    
    
	//友元函数声明
	//friend void operator<< (ostream& out, const Date& d);
	friend ostream&  operator<< (ostream& out, const Date& d);

	friend istream& operator>> (istream& in, Date& d);


public:
	//构造函数 
	Date(int year = 1, int month = 1, int day = 1);

	//内联 
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	//声明和定义分离 
	bool operator< (const Date& x);
	bool operator== (const Date& x);

	bool operator<= (const Date& x);
	bool operator> (const Date& x);
	bool operator>=  (const Date& x);
	bool operator!=  (const Date& x);

static	int GetMonthDay(int year ,int month);
	Date& operator+= (int day);
	Date operator+ (int day);
	Date& operator++();  //前置++
	Date  operator++(int );//后置++

  Date&	operator-= (int day);
  Date operator- (int day);
  Date& operator-- (); //前置-- ,返回--之后的值
  Date operator--(int); //后置 -- ,返回--之前的值 

 int   operator- (const Date & d); //d1 -d2 


 //流插入不能写成成员函数 
 //因为Date对象默认占了第一个参数,就做了左操作数
 // 写出来就一定是下面这样子 ,不符合使用习惯 
// d1 << cout; // d1.operator << cout

 //void operator<< (ostream& out);

 int GetYear()
 {
    
    
	 return this->_year;
	}

private:
	//内置类型
	int _year;
	int _month;
	int _day;
};

//void operator<< (ostream& out);

ostream& operator<< (ostream& out, const Date& d);

istream& operator>> (istream& in, Date& d);


Date.cpp

#include"Date.h"
//构造函数 
Date::Date(int year , int month , int day )
{
    
    
	if (month > 0 && month < 13
		&& day > 0 && day <=GetMonthDay(year, month)  )
	{
    
    

		this->_year = year;
			this->_month = month;
			this->_day = day;
	}
	else
	{
    
    
		cout << "非法日期"<<endl;
	}
}
bool Date::operator< (const Date& x)
{
    
    
	//bool Date::operator ( Date * const this , const Date &x )
	if (this->_year < x._year)
	{
    
    
		return true;
	}
	else if (this->_year == x._year && this->_month < x._month)
	{
    
    
		return true;
	}
	else if (this->_year == x._year && this->_month == x._month && this->_day < x._day)
	{
    
    
		return true;
	}

	return false;
}


bool Date:: operator== (const Date& x)
{
    
    
	return this->_year == x._year
		&&	this->_month == x._month
		&&  this->_day == x._day;
}




//bool Date::operator<= ( Date * const this, const Date& x)
//假设 d1<=d2  ,d1 是*this ,d2 是x

bool Date::operator<= (const Date& x)
{
    
    
	//复用之前的函数 
	return  *this < x  ||  *this ==x ;
}
bool Date::operator> (const Date& x)
//bool Date::operator> ( Date *this , const Date &x )
//d1 >d2 ,d1 是*this ,d2 是x
{
    
    
	return !(*this<=x);
}
bool Date::operator>=  (const Date& x)
{
    
    
	return !(*this < x);
}
bool Date::operator!=  (const Date& x)
{
    
    
	return !(*this == x);
}

int Date::GetMonthDay(int year, int month)
{
    
    
	static int daysArr[13] = {
    
     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	//是闰年 
	if  (month == 2 && (  (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) )) 
	{
    
    
		//四年一闰,百年不闰,四百年再闰
		return 29;
	}
	else
	{
    
    
		return daysArr[month];
	}
}


Date& Date::operator+= (int day)
//Date Date::operator+ (Date *  const this ,int day)
{
    
    
	if (day < 0)
	{
    
    
		return *this -= -day;
	}
	this->_day += day;
    
	while (   this->_day   >     GetMonthDay( this->_year,  this->_month)  )
	{
    
    
		this->_day -= GetMonthDay(this->_year, this->_month);
		this->_month++;
		if (this->_month == 13)
		{
    
    
			this->_year++;
			this->_month = 1;
		}
	}
	return *this;
}




Date Date::operator+ (int day)//后置++
//Date Date::operator+ (Date* const this ,int day  )
{
    
    
	Date tmp(*this);//用已存在的对象*this拷贝创建tmp 

	//tmp._day += day;

	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	//{
    
    
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
	//	tmp._month++;
	//	if (tmp._month == 13)
	//	{
    
    
	//		tmp._year++;
	//		tmp._month = 1;
	//	}
	//}

	//复用函数 
	tmp += day;
	return tmp;
}


//前置++
Date& Date::operator++()
//Date& Date::operator++( Date * const this )
{
    
    
	
	*this += 1;

	return *this;
}

//后置++
//增加这个int参数不是为了具体的值 ,仅仅是占位 ,和前置++构成函数重载 
Date Date::operator++(int)
{
    
    
	Date tmp = *this; //
	*this += 1;
	return tmp;  // tmp出了作用域就被销毁 ,用传值返回 
}

Date& Date::operator-= (int day)
//Date Date::operator( Date * const this  ,int day )
{
    
    

	if (day < 0)
	{
    
    
		return *this += -day;
	}


	this->_day -= day;
    
	while (this->_day <=0)
	{
    
    
		--this->_month;
		if (this->_month == 0)
		{
    
    
			this->_month = 12;
			--this->_year;
		}
		this->_day += GetMonthDay(this->_year ,this->_month);
	}
	return *this ;//出了函数作用域,this指针指向的对象没有被销毁。
}

Date Date::operator- (int day)
//Date Date::operator-(int day )
{
    
    

	Date(tmp) = *this;

	tmp-=day;

	return tmp;

}
Date& Date::operator-- () //前置--
{
    
    
 //前置-- ,返回--之后的值 
	*this -= 1;;
	return *this;

}
Date Date::operator--(int) //后置 -- ,返回--之前的值 
{
    
    
	Date tmp = *this;//拷贝构造
	*this -= 1;
	
		return tmp; 
}

int   Date::operator- (const Date& d) //d1-d2
//int   operator- (  Date * const this,const Date& d) //d1-d2
{
    
    
	//假设 *this 比 d 大
	Date max = *this;
	Date min = d;
	int flag = 1;
	if (*this < d) 
	{
    
    
		Date max = d;
		Date min = *this;
		int flag = -1;
	}
	
	int n = 0;
	while (max!=min)
	{
    
    
		
		++min;
		++n;
	 }

	return  flag * n;
}
//void Date::operator<< (ostream& out) //out 是cout 的别名 
//
void Date::operator<< (Date* const this ,stream& out) //out 是cout 的别名 
//{
    
    
//	out <<this->_year << "年" << this->_month << "月" << this->_day << "日" << endl;
//}


//void operator<< (ostream& out ,const Date &d)
//{
    
    
//	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
//
//}

ostream & operator<< (ostream& out, const Date& d)
{
    
    
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;// out 就是cout 
}

istream& operator>> (istream& in, Date& d)

{
    
    
	
	
	int  month, year, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day>0 && day <=Date::GetMonthDay(year, month))
	{
    
    
		d._year = year;
		d._month = month;
		d._day = day;
	}
	return in;
}

Advanced

insert image description here

Address and const address operator overloading

These two default member functions generally do not need to be redefined, and the compiler will generate by default

class Date
{
    
    
 
public:
	Date* operator&()
	{
    
    
		return this;
	}
 
	const Date* operator&()const
 
	{
    
    
		return this;
	}
 
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
 
};

These two operators generally do not need to be overloaded, just use the default address overload generated by the compiler, unless you want others to get the specified content!

	const Date* operator&()const
 
	{
    
    
		return this;
	}

If you need this as the return value, you need to overload the const modification address operator, because this is modified by const in front of it, and it must be modified by const as the return value

Let's talk about the constructor

constructor body assignment

When creating an object, the compiler will give each member variable in the object an appropriate initial value by calling the constructor:

class Date
{
    
    
public:
	// 构造函数
	Date(int year = 0, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

It should be noted that although each member variable in the object has an initial value after calling the above constructor, the statement in the constructor can only be called initial value assignment, not initialization. Because initialization can only be initialized once, and multiple assignments can be made in the constructor body.

class Date
{
    
    
public:
	// 构造函数
	Date(int year = 0, int month = 1, int day = 1)
	{
    
    
		_year = year;// 第一次赋值
		_year = 2022;// 第二次赋值
		//...
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

initialization list

Initialization list: Starts with a colon, followed by a comma-separated list of data members, each member variable followed by an initial value or expression enclosed in parentheses.

class Date
{
    
    
public:
	// 构造函数
	Date(int year = 0, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
    
    }
private:
	int _year;
	int _month;
	int _day;
};

1. Each member variable can only appear at most once in the initialization list

Because initialization can only be done once, the same member variable cannot appear more than once in the initialization list.

2. The class contains the following members, which must be placed in the initialization list for initialization:
reference member variable

class B
{
    
    
public:
	//初始化列表:对象成员定义的位置 
	B(int a, int ref)
		//初始化 
		:_ref(ref)
		,_n(1)

	{
    
    
		//赋值
		;
	}
private:
	//声明 
	//特征:必须在定义的时候初始化
	int& _ref;//引用
	const int _n; //const
};
int main()
{
    
    
	//对象整体定义
	B bb1(10, 1);
	B bb2(11, 2);
	return 0;
}

const member variable

Variables modified by const must also be given an initial value when they are defined, and must also be initialized using an initialization list.

class B
{
    
    
public:
	//初始化列表:对象成员定义的位置 
	B(int a, int ref)
		//初始化 
		:_ref(ref)
		,_n(1)

	{
    
    
		//赋值
		;
	}
private:
	//声明 
	//特征:必须在定义的时候初始化
	int& _ref;//引用
	const int _n; //const
};
int main()
{
    
    
	//对象整体定义
	B bb1(10, 1);
	B bb2(11, 2);
	return 0;
}

custom type member (the class has no default constructor)
If a class does not have a default constructor, then we need to pass parameters to initialize it when instantiating the class object, so when instantiating a class object without a default constructor, we must use the initialization list to initialize it.

class  A
{
    
    
public:
	//构造函数 
	A(int a =0) //给了缺省值 
		:_a(a)
	{
    
    
		cout << "A(int a = 0 )" << endl;
	}
private:
	int _a;

};
class B
{
    
    
public:
	//初始化列表:对象成员定义的位置 
	B(int a, int ref)
		//初始化 
		:_ref(ref)
		,_n(1)
		,_x(2)
		,_aobj(10)

	{
    
    
		//赋值
		;
	}
private:
	//声明 
	//特征:必须在定义的时候初始化
	A _aobj;  //自定义类型 , 没有默认构造函数 
	int& _ref;//引用
	const int _n; //const
	int _x =1; //缺省值  ,这个缺省值是给初始化列表的
};
int main()
{
    
    
	//对象整体定义
	B bb1(10, 1);
	B bb2(11, 2);
	return 0;
}

Summarize:

Custom type members, const member variables, and reference member variables all have a characteristic: they must be initialized at the time of definition, and the variable types that must be initialized at the time of definition must be placed in the initialization list for initialization

References: References also have only one chance to be initialized, also in the place where it is defined.

[Note]: Each member must go through the initialization list, because the initialization list is where the member is defined.
Even if it is not displayed in the initialization list, it will still go through the initialization list.
For built-in types, if there is a default value, use the default value, and if there is no default The value compiler will process it into a random value;
for a custom type, call its default construction, and report an error if there is no default construction.

3. Try to use initialization list initialization

When instantiating an object, the place where the object's member variables are defined is the initialization list. Whether you want to use the initialization list or not, you will go through such a process (the member variables need to be defined).

  • If it is a built-in type, there is almost no difference between using the initialization list and initializing in the constructor body (without using the initialization list), such as the following code:
// 使用初始化列表
int a = 10
// 在构造函数体内初始化(不使用初始化列表)
int a;
a = 10;

  • If it is a custom type, using an initialization list can improve the efficiency of the code
class Time
{
    
    
public:
	Time(int hour = 0)
	{
    
    
		_hour = hour;
	}
private:
	int _hour;
};
class Test
{
    
    
public:
	// 使用初始化列表
	Test(int hour)
		:_t(12)// 调用一次Time类的构造函数
	{
    
    
	}
private:
	Time _t;
};

For the above code, when an object of the Test class needs to be instantiated, the initialization list is used, and the constructor of the Time class is only called once during the instantiation process .
 If you want to achieve the desired effect without using the initialization list, just change it to this:

class Time
{
    
    
public:
	Time(int hour = 0)
	{
    
    
		_hour = hour;
	}
private:
	int _hour;
};
class Test
{
    
    
public:
	// 在构造函数体内初始化(不使用初始化列表)
	Test(int hour)
	{
    
     //初始化列表调用一次Time类的构造函数(不使用初始化列表但也会走这个过程)
		Time t(hour);// 调用一次Time类的构造函数
		_t = t;// 调用一次Time类的赋值运算符重载函数
	}
private:
	Time _t;
};

To instantiate an object of the Test class, in the instantiation process, the constructor of the Time class will be called once when the list is initialized, then the constructor of the Time class will be called once when the t object is instantiated, and finally the Time class will be called once The assignment operator overloads the function, calls it multiple times, and the efficiency drops.
4. The order in which member variables are declared in the class is the order in which they are initialized in the initialization list, and has nothing to do with their order in the initialization list.

class A
{
    
    
public:
	//构造函数 
	A(int a)
		//初始化列表,按照声明的顺序初始化
		:_a1(a)
		, _a2(_a1)
	{
    
    
	}
	void Print() 
	{
    
    
		cout << _a1 << " " << _a2 << endl;
	}
private:
	//声明
	int _a2;
	int _a1;
};
int main()
{
    
    
	A aa(1);//调用构造函数 
	aa.Print();//1 随机值
}

In the member variable, _a2 is declared first, and _a1 is declared later. According to the initialization order of the initialization list, the member variable is declared in the class, so in the initialization list of the class A constructor, the member variable _a2 is initialized first, and the member variable _a1 post-initialization, i.e. the final result is 1 and a random value

Suggestion: The order of declarations is consistent with the order of definitions

explicit keyword

Constructors can not only construct and initialize objects, but also have the function of type conversion for a single parameter or a constructor with default values ​​except for the first parameter, which has no default value.

#include <iostream>
using namespace std;
class Date
{
    
    
public:
	Date(int year = 0) //单个参数的构造函数
		:_year(year)
	{
    
    
	}
	void Print()
	{
    
    
		cout << _year << endl;
	}
private:
	int _year;
};
int main()
{
    
    
	Date d1 = 2023; //2023先构造一个Date的临时对象,临时对象再拷贝构造d1——>编译器会优化成用2直接构造
	d1.Print();
	return 0;
}

In the early compiler, when encountering the code Date d1 = 2023, it will first construct a temporary object of Date, and then use the temporary object to copy and construct d1; with the improvement of the compiler: construction + copy construction ---- > optimized to: direct construction

So here, in order to avoid implicit type conversion, the constructor is modified with explicit. It will be processed according to the code of Date d1(2023), which is called implicit type conversion.

int a = 10;
double b = a; //隐式类型转换

In this process, the compiler will first construct a temporary variable of double type to receive the value of a, and then assign the value of the temporary variable to b. This is why a function can return the value of a local variable, because when the function is destroyed, although the variable used as the return value is also destroyed, the temporary variable generated during the implicit type conversion is not destroyed, so the value remains exist.

insert image description here

However, for a single-parameter custom type, the code of Date d1 = 2023 is not very readable. If we want to prohibit the implicit conversion of the single-parameter constructor, we can use the keyword explicit to modify the constructor.

#include <iostream>
using namespace std;
class Date
{
    
    
public:
 explicit	Date(int year = 0) //单个参数的构造函数
		:_year(year)
	{
    
    
	}
	void Print()
	{
    
    
		cout << _year << endl;
	}
private:
	int _year;
};
int main()
{
    
    
	Date d1 = 2023; //2023先构造一个Date的临时对象,临时对象再拷贝构造d1——>编译器会优化成用2直接构造
	d1.Print();
	return 0;
}

In the same expression, the compiler will basically optimize consecutive structures, in order to improve efficiency

static member

concept

Class members declared static are called static members of the class. Member variables modified with static are called static member variables; member functions modified with static are called static member functions. Static member variables must be initialized outside the class.

characteristic

1. Static members are shared by all class objects and do not belong to a specific object

class A
{
    
    
public:
	//构造函数 
	A()
	{
    
    
		++_scount;
	}
	//拷贝构造函数 
	A(const A& aa)
	{
    
    
		++_scount;
	}
	//析构函数 
	~A()
	{
    
    
		--_scount;
	}
	static int   GetACount()
	{
    
    
		return _scount;
	}
private:
	//都是声明 
	//静态成员变量 属于类,属于类的每个对象共享 ,存储在静态区 ,生命周期和全局变量一样
  static	int _scount;
  //成员变量属于每一个类对象 ,存储在对象里面
  int _a1 = 1;
  int _a2 = 1;
};
//全局位置,必须在类外面定义
int A::_scount = 0;
int main() 
{
    
    
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);//拷贝构造 
	cout << A::GetACount() << endl;
	return 0; 
}

Static member_n is stored in the static area, belongs to the entire class, and is also shared by each object of the class. When calculating the size of a class or the size of a class object, static members are not included in the sum of their total sizes

Member variables belong to each class object, stored inside the object

2. Static member variables must be defined outside the class, without adding the static keyword when defining

class Test
{
    
    
private:
	static int _n;
};
// 静态成员变量的定义初始化
int Test::_n = 0;

Note: Although the static member variable _n is private here, we directly access it outside the class by breaking through the class domain. This is a special case and is not restricted by access qualifiers, otherwise there is no way to define and initialize static member variables.

3. Static member functions have no hidden this pointer and cannot access any non-static members

class Test
{
    
    
public:
    //没有this指针 ,指定类域和访问限定符就可以访问
	static void Fun()
	{
    
    
		cout << _a << endl; //error不能访问非静态成员
		cout << _n << endl; //correct
	}
private:
	int _a; //非静态成员
	static int _n; //静态成员
};

Classes with static member variables generally contain a static member function for accessing static member variables. That is to say, general static member functions and static member variables appear together.
4.1 When static member variables are public, there are the following access methods:

#include <iostream>
using namespace std;
class Test
{
    
    
public :
	static int _n;

};
// 静态成员变量的定义初始化
int Test::_n = 0;
int main()
{
    
    
	Test test; //创建对象 
	cout << Test::_n << endl;  //通过类名突破类域
	cout << test._n << endl; //通过类对象突破类域
	cout << Test()._n << endl; //通过匿名对象突破类域 
	return 0;
}

4.2 When the static member variable is private, there are the following access methods:

#include <iostream>
using namespace std;
class Test
{
    
    
public:
	static int GetN()
	{
    
    
		return _n;
	}

private:
	static int _n;
};
//静态成员变量的定义初始化
int Test::_n = 0;
int main()
{
    
    
	Test test; //创建对象 
	//通过对象调用成员函数 
	cout << test.GetN()<<endl;
	//通过匿名对象调用成员函数 
	cout << Test().GetN << endl;
	//通过类名调用成员函数 
	cout << Test::GetN << endl; 
	return 0;
}

5. Static members, like ordinary members of a class, also have three access levels: public, private, and protected.
 Therefore, when a static member variable is set to private, even if we break through the class domain, we cannot access it.

Can a static member function call a non-static member function?

:Can't. Because the first formal parameter of a non-static member function defaults to the this pointer, and there is no this pointer in a static member function, a static member function cannot call a non-static member function.


Can a non-static member function call a static member function?

:Can. Because both static member functions and non-static member functions are in the class, they are not restricted by the access qualifier in the class.

Tomomoto

Friend is divided into friend function and friend class. Friends provide a way to break out of encapsulation, and sometimes convenience. But friends will increase the degree of coupling and destroy the encapsulation, so friends should not be used more often.

friend function

A friend function can directly access the private members of the class. It is an ordinary function defined outside the class and does not belong to any class, but it needs to be declared inside the class, and the friend keyword needs to be added when declaring.

Problem:
Now try to overload the operator, and then find that there is no way to overload the operator into a member function. Because the output stream object of cout and the implicit this pointer are preempting the position of the first parameter. The this pointer defaults to the first parameter, which is the left operand. But in actual use, cout needs to be the first formal parameter object to be used normally. So the operator must be overloaded as a global function. But it will cause no way to access members outside the class, and friends are needed to solve it. operator>>The same reason.

class Date
 
{
    
    
 
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{
    
    }
	// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
 
	// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
 
	ostream& operator<<(ostream& _cout)
	{
    
    
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

A friend function can directly access the private members of the class. It is an ordinary function defined outside the class and does not belong to any class, but it needs to be declared inside the class, and the friend keyword needs to be added when declaring.

class Date
 
{
    
    
  //声明 
 friend ostream& operator<<(ostream& _cout, const Date& d);
 friend istream& operator>>(istream& _cin, Date& d);
public:
 Date(int year = 1900, int month = 1, int day = 1)
 : _year(year)
 , _month(month)
 , _day(day)
 {
    
    
 }
 
private:
 int _year;
 int _month;
 int _day;
};
 
ostream& operator<<(ostream& _cout, const Date& d)
{
    
    
 _cout << d._year << "-" << d._month << "-" << d._day;
 return _cout; 
}
 
istream& operator>>(istream& _cin, Date& d)
{
    
    
 _cin >> d._year;
 _cin >> d._month;
 _cin >> d._day;
 return _cin;
}
 
int main()
{
    
    
 Date d;
 cin >> d;
 cout << d << endl;
 return 0;
}

Notice:

1. The friend function can access the private and protected members of the class, but not the member functions of the class.
 2. Friend functions cannot be modified with const.
 3. Friend functions can be declared anywhere in the class definition and are not restricted by access qualifiers.
 4. A function can be a friend function of multiple classes.
 5. The principle of calling a friend function is the same as that of an ordinary function.

friend class

All member functions of a friend class can be friend functions of another class and can access non-public members of another class

class Time
{
    
    

	//声明友元类,声明放在那个位置都是一样的,
	//只有成员才会受私有或者共有的限制 ,访问限定符限制的是一个成员的访问方式,成员包含成员变量和成员函数 
	friend class Date;
public:
	Time(int hour = 10, int minute = 50, int  second = 40)
		:_hour(hour)
		, minute(minute)
		, _second(second)
	{
    
    

	}
private:
	int _hour;
	int minute;
	int _second;
};
class Date
{
    
    
	
public :
	Date(int year = 2023 ,int month =5 ,int day =22)
		:_year(year)
		,_month(month)
		,_day (day )
	{
    
    

	}

	void SetTimeOfDate(int hour, int minute, int second)
	{
    
    
		_t._hour = hour;
		_t.minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

inner class

concept:

If a class is defined inside another class, this inner class is called an inner class. An inner class
is an independent class that does not belong to an outer class, let alone access members of an inner class through an object of an outer class. Outer classes do not have any privileged access to inner classes.

[Note]: The inner class is the friend class of the outer class. See the definition of the friend class. The inner class can access all members of the outer class through the object parameters of the outer class. But the outer class is not a friend of the inner class.

characteristic:

  1. The inner class can be defined in the public, protected, and private of the outer class.

  2. Note that the inner class can directly access the static members in the outer class without the object/class name of the outer class.

  3. sizeof(outer class) = outer class, has nothing to do with inner class.

class A
{
    
    
public:
//private:
	//内部类

	//B在A的类域里面,但是B不占空间 
	class B
	{
    
    
	public:
		//内部类是外部类的友元
		void foo(const A & a)
		{
    
    
			cout << k << endl;
			cout << a.h << endl;
		}

	};
private:
	//成员变量 
	int h;
	//静态成员变量 
	static int k;//不占空间 ,静态成员变量不在对象里面 
};
//静态成员变量定义初始化 
int A::k = 1;

int main()
{
    
    
	A::B bb;
	cout <<sizeof(A)<<endl;
	return 0;
}

anonymous object

class A
{
    
    
public:
	A(int a = 0)
		:_a(a)
	{
    
    
		cout<< "	A(int a = 0)" << endl;
	}

	~A()
	{
    
    
		cout << "~A()" << endl;
	}
private:
	int _a;
};
class Solution
{
    
    
public:
	int Sum_Solution( int n)
	{
    
    
		cout << "Sum_Solution" << endl;

		return n;
	 }

};


int main()
{
    
    
	A aa(1) ; //有名对象 ,生命周期在当前函数的局部域
	A(2);//匿名对象  ,生命周期在当前行

	Solution sl;//有名对象调用 
	sl.Sum_Solution(10);

	//匿名对象调用 ,即用即销毁

	Solution().Sum_Solution(20);
	//匿名对象具有常性
	//A& ra = A(1);//err
	const A& ra = A(1); //const引用延长了匿名对象的生命周期 ,生命周期在当前函数的作用域 
	return 0;
}

insert image description here

If you think this article is helpful to you, you might as well move your fingers to like, collect and forward, and give Xi Ling a big attention. Every
support from you will be transformed into my motivation to move forward! ! !

Guess you like

Origin blog.csdn.net/qq_73478334/article/details/130543177