C++ Elementary: Classes and Objects (2)

1. The six default member functions of the class

If there are no members in a class, it is simply called an empty class.
Is there nothing in the empty class? No, any class will automatically generate the following 6 default member functions if we don't write it.
Default member function: The member function generated by the compiler without explicit implementation by the user is called the default member function.

class Date {
    
    };

insert image description here

2. Constructor

(1) Concept

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

(2) Features

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 function name is the same as the class name.

(2) No return value.

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

(4) The constructor can be overloaded.

#include<iostream>
using namespace std;
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:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
 
int main()
{
    
    
 
	Date d1;	//无参写法
	d1.Print();
 
	Date d2(2022, 5, 15);	//含参写法,全缺省函数传3个参数可以
	d2.Print();
 
 
	return 0;
}

insert image description here

class Date
{
    
    
public:
	//Date()    //不传参写法(不建议这么写)
	//{
    
    
	//	_year = 1;
	//	_month = 1;
	//	_day = 1;
	//}
 
	//Date(int year, int month, int day)    //传参写法(不建议这么写)
	//{
    
    
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
 
	Date(int year = 1, int month = 1, int day = 1)	//全缺省写法是最佳写法,相传几个参数都行
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
 
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
 
int main()
{
    
    
	//Date d1(); // 无参不能这么写,编译器无法区分函数声明还是类的定义
	Date d1;	//无参写法
	d1.Print();
 
	Date d2(2022, 5, 15);	//含参写法,全缺省函数传3个参数可以
	d2.Print();
 
	Date d3(2022);    //全缺省函数传1个参数可以
	d3.Print();
 
	Date d4(2022, 10);    //全缺省函数传2个参数可以
	d4.Print();
 
	return 0;
}

If the object is created through a no-argument constructor, the object does not need to be followed by parentheses, otherwise it becomes a function declaration.
The function of the following code: the d3 function is declared, which has no parameters and returns an object of type date.
warning C4930: "Date d3(void)": prototype function not called (was it intentionally defined with a variable?)

(5) If there is no constructor explicitly defined in the class, the C++ compiler will automatically generate a default constructor without parameters. Once the user explicitly defines the compiler, it will no longer generate it.

1. Built-in types/basic types: int/char/double/pointer...
2. Custom types: class/struct to define type objects
3. The default generated constructor does not process built-in type member variables, and custom type member variables will be processed

class Date{
    
    
public:
	 如果用户显式定义了构造函数,编译器将不再生成
	//Date(int year, int month, int day){
    
    
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	void Print(){
    
    
	
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
     
	Date d1;
	return 0;
}

The above program executes successfully.

class Date{
    
    
public:
	// 如果用户显式定义了构造函数,编译器将不再生成
	Date(int year, int month, int day){
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print(){
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
     
	Date d1;
	return 0;
}

1. After masking the constructor in the Date class, the code can be compiled because the compiler generates a default constructor with no parameters.
2. Let go of the constructor in the Date class, and the code will fail to compile, because once any constructor is explicitly defined, the compiler will no longer generate it.
3. No-argument constructor, an error will be reported after release: error C2512: “Date”: no suitable default constructor is available.

When we don't design the constructor ourselves, the compiler will generate a constructor by itself. Let's take a look at what the constructor generated by the compiler will do.

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

insert image description here

If no constructor is implemented, the compiler will generate a default constructor. But it seems that the default constructor is useless? The d object calls the default constructor generated by the compiler, but the d object _year/_month/_day is still a random value. In other words, the default constructor generated by the compiler is useless here? ?
Answer: C++ divides types into built-in types (basic types) and custom types. The built-in type is the data type provided by the language, such as: int/char..., the custom type is the type we define ourselves using class/struct/union, etc. If you look at the following program, you will find that the default constructor generated by the compiler will be Its default member function called on the custom type member _t.

class Time
{
    
    
public:
	Time()
	{
    
    
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
 
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
public:
	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
	
};
int main()
{
    
    
	Date d;
	d.Print();
	return 0;
}

insert image description here
"Print()" is printed here, which means that the compiler calls its constructor for the _t member variable, which means that we do not implement the constructor by ourselves in the d object, and the constructor implemented by the compiler only Process the member variables of custom types (call their corresponding constructors), and do not process any built-in types.

Regarding the above results, we should have a doubt. If we just don't implement the constructor, is there any other way for us to initialize the member variables of the built-in type besides manually implementing the constructor?

In C++11, a patch has been applied to address the defect of non-initialization of built-in type members, namely: built-in type member variables can be given default values ​​when declared in a class. As the following code:

class Date
{
    
    
public:
	void Print()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	
private:
	// 基本类型(内置类型)
	int _year = 2022;
	int _month = 2;
	int _day = 5;
};
int main()
{
    
    
	Date d;
	d.Print();
	return 0;
}

insert image description here

3. Questions about constructors

Which of the following descriptions about constructors is correct ( )

A. A constructor can declare a return type

B. The constructor cannot be modified with private

C. The constructor must have the same name as the class

D. The constructor cannot take parameters

Answer analysis:

A. The constructor cannot have a return value, including the void type

B. The constructor can be private, but then the object cannot be instantiated directly afterwards

C. It is necessary

D. The constructor can not only take parameters, but also have multiple constructors to form an overload

So choose C

(6) Default constructor: Both the parameterless constructor and the full default constructor are called default constructors, and there can only be one default constructor.

class Date
{
    
    
public:
	Date()
	{
    
    
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
int main(){
    
    
{
    
    
	Date d1;
}

(6) Default constructor: Both the parameterless constructor and the full default constructor are called default constructors, and there can only be one default constructor.

No-argument constructors, full default constructors , and constructors that we did not write to be generated by the compiler by default can all be considered default constructors. Therefore, when an object is instantiated, the compiler cannot distinguish which constructor to call, which is why the default constructor can only be associated with one.

The actual list of parameters we specify to the d1 object:

class Date{
    
    
public:
	Date(){
    
    
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1){
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
int main(){
    
    
	//给出实际参数
	Date d1(10,200,300);
}

At this time, it can be successfully compiled again. When we write the parameters, we also specify the call constructor for the compiler.

3. Destructor

Do you still remember the question of Leetcode's effective parentheses? We use the stack structure to match parentheses.

The main logic code in it:

bool isValid(char * s){
    
    
    ST* stact =StactInit();
    while(*s)
    {
    
    
        if(*s=='('||*s=='{'||*s=='[')
        {
    
    
            StactPushBank(stact,*s);
            s++;
        }
        else
        {
    
    
            if(StactEmpty(stact))
            {
    
    
                StactDestory(stact);
                return false;
            }
            char ch=StactTop(stact);
            StactPopBank(stact);
            if((ch=='(' && *s==')')||
               (ch=='[' && *s==']')||
                ch=='{' && *s=='}')
                {
    
    
                    s++;
                }
                else
                {
    
    
                    StactDestory(stact);
                    return false;
                }
        }
    }
    if(!StactEmpty(stact))
    {
    
    
        StactDestory(stact);
        return false;
    }
    StactDestory(stact);
    return true;
}

We have to fully consider various possible ending situations, so it is very uncomfortable to destroy the stack before each end.

In C++, this problem is very well solved-the destructor.

(1) Concept

Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself, and the local object destruction is done by the compiler. When the object is destroyed, it will automatically call the destructor to complete the cleanup of resources in the object.

(2) Characteristics

A destructor is a special member function whose characteristics are as follows:

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

2. No parameters and no return value type.

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

4. When the object life cycle ends, the C++ compilation system automatically calls the destructor.

typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 3)
	{
    
    
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
 
	//析构函数
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
	
private:
	DataType* _array;
	int _capacity;
	int _size;
};

5. Regarding the destructor automatically generated by the compiler, will something be done?
In the following program, we will see that the default destructor generated by the compiler calls its destructor for the self-defined type members.

class Time
{
    
    
public:
	~Time()
	{
    
    
		cout << "Time()" << endl;
	}
private:
	int _hour=0;
	int _minute=0;
	int _second=0;
};
class Date
{
    
    
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d();
	return 0;
}

insert image description here
After the program runs, the output: ~Time()
does not directly create an object of the Time class in the main method at all, why does it call the destructor of the Time class at the end?

Because:
1. The Date object d is created in the main method, and d contains 4 member variables, among which _year, _month, and _day are built-in type members, and resource cleaning is not required when destroying, and finally the system directly reclaims its memory That's it.
2. _t is an object of the Time class, so when d is destroyed, the _t object of the Time class contained inside it must be destroyed, so the destructor of the Time class must be called.
3. However: the destructor of the Time class cannot be called directly in the main function. What actually needs to be released is the Date class object, so the compiler will call the destructor of the Date class. If Date is not explicitly provided, the compiler will give The Date class generates a default destructor, the purpose of which is to call the destructor of the Time class inside it, that is, when the Date object is destroyed, it is necessary to ensure that each custom object inside it can be correctly destroyed and not directly called in the main function Time class destructor, and instead explicitly call the compiler-generated default destructor for the Date class.
4. Note: Create an object of which class to call the constructor of that class, and destroy an object of that class to call the destructor of that class.

6. If there is no resource application in the class, the destructor can not be written, and the default destructor generated by the compiler, such as the
Date class, should be used directly; when there is a resource application, it must be written, and the correct free() we applied for Space, otherwise it will cause a memory leak, such as the Stack class.

4. Copy constructor

If we need to instantiate two identical objects, we need to call the object's copy constructor.
insert image description here

(1) Concept

If you want to copy an object in C++, you can only call its corresponding copy constructor.

General concept: When creating a new object, completely copy an existing object to the new object: Date d2(d1)

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 is automatically called by the compiler when creating a new object with an existing class type object.

(2) Characteristics

The copy constructor is also a special member function with the following characteristics:

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

2. The parameter of the copy constructor is only one and must be a reference to a class type object, and the compiler will report an error directly when using the pass-by-value method, because it will cause infinite recursive calls.

The parameter of the copy constructor has only one parameter, and it must be a reference to an object of class type. If it is a value, it will cause infinite function recursive calls.
Reason: We know that when a function is called by value, the formal parameter is a temporary copy of the actual parameter. If the parameter is a class type, the corresponding copy constructor must be called, and the call to the copy constructor must pass the class Type parameters, but also call the copy constructor ~~~~ **
insert image description here

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

class Time
{
    
    
public:
	Time()
	{
    
    
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	//拷贝构造函数
	Time(const Time& t)
	{
    
    
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
public:
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d1;
 
	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
	Date d2(d1);
	d1.show();
	d2.show();
	return 0;
}

insert image description here

Explanation: Date d2 (d1): use the existing d1 to copy and construct d2, here the copy constructor of the Date class will be called, if the copy constructor is not explicitly defined, the compiler will generate a default copy constructor for the Date class function. The output "Time::Time(const Time&)" proves that the custom type is copied by calling its copy constructor;
note: this is also a copy construction:
Date d2 = d1;

Note: In the default copy constructor generated by the compiler, the built-in type is directly copied in byte mode, while the custom type is copied by calling its copy constructor.

4. The default copy constructor generated by the compiler can already copy byte-ordered values. Do I need to explicitly implement it myself? Of course classes like the Date class are unnecessary. What about the following classes? Try to verify it?

5. Typical calling scenarios of copy constructor:

  • Create a new object using an existing object
  • The function parameter type is a class type object
  • The return value type of the function is a class type object
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);
}

//3. 以值方式返回局部对象
Person doWork2()
{
    
    
	Person p1;
	cout << (int *)&p1 << endl;
	return p1;
}

void test03()
{
    
    
	Person p = doWork2();
	cout << (int *)&p << endl;
} 
int main() {
    
    
	//test01();
	//test02();
	test03();

	system("pause");
	return 0;
}

(3) Deep copy and shallow copy

  1. Shallow copy: simple assignment copy
  2. Deep copy: re-apply for space in the heap area and perform copy operations

Example:

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;
}

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

Five. Operator overloading

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name, and also has its
return value type, function name, and parameter list. The return value type and parameter list are similar to ordinary functions.

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).

If we need to implement a date plus a number of days to get a new date, according to our previous way of thinking, it is to implement a function, but using operator overloading will become more intuitive and clear

class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	
	int _year;
	int _month;
	int _day;
};
//函数判断日期相等
bool DateEqual(const Date& d1, const Date& d2)
{
    
    
	return d1._year == d2._year &&
		d1._month == d2._month &&
		d1._day == d2._day;
}
//运算符重载的定义 使用关键字 operator 【运算符】
bool operator==(const Date& d1 ,const Date& d2)
{
    
    
	return d1._year == d2._year &&
		   d1._month == d2._month && 
		   d1._day == d2._day;
}
int main()
{
    
    
	Date d2(2022, 4, 23);
	Date d1(2022, 1, 13);
	//函数使用
	if (DateEqual(d1, d2))
		cout << "true" << endl;
	else
		cout << "false" << endl;
 
	//运算符重载的使用
	if (d1 == d2)
		cout << "true" << endl;
	else
		cout << "false" << endl;
	
	return 0;
}

insert image description here
But generally our member variables are private. If they are private, we cannot directly access them. We can solve this by adding a get function, or by writing the operator overloaded function into the class to become a member function. Here we are Encapsulated directly into the class:

class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
 
    // bool operator==(Date* this, const Date& date)
    // 这里需要注意的是,左操作数是this,指向调用函数的对象
	bool operator ==(const Date& date)
	{
    
    
		return _year == date._year &&
               _month == date._month && 
               _day == date._day;
	}
            
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d2(2022, 1, 13);
	Date d1(2022, 1, 13);
	//运算符重载的使用
	if (d1 == d2)
		cout << "true" << endl;
	else
		cout << "false" << endl;
	
	return 0;
}

Note: When operator overloading is written as a member member function, the overloaded operator only needs to write one parameter, because one parameter is the object itself, which is the this pointer hidden in the member function list.

6. Assignment operator overload format:

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

2. Return value type: T&, return reference can improve the efficiency of return, the purpose of return value is to support continuous assignment to detect whether to assign a value to itself

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

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
    //赋值运算符重载
	Date& operator=(const Date& d)
	{
    
    
        //如果不是自身赋值,即d1=d1,才需要成员变量对用赋值。
		if (this != &d)
		{
    
    
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date date1;
	Date date2(2023, 2, 7);
	date1.show();
	//因为有返回值就可以实现链式访问
	(date1 = date2).show();
	return 0;
}

insert image description here
4. Assignment operators can only be overloaded as member functions of classes and cannot be overloaded as global functions

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;
}

The assignment operator is overloaded into a global function. Note that there is no this pointer when overloading into a global function. Two parameters need to be given

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.

5. When the user does not explicitly implement, the compiler will generate a default assignment operator overload, which is copied byte by byte in the form of value.

class Time
{
    
    
public:
	Time()
	{
    
    
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{
    
    
		if (this != &t)
		{
    
    
			cout << "Time:operator=" << endl;
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}
		return *this;
	}
	
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	// 基本类型(内置类型)
	int _year ;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d1;
	d1.show();
	Date d2(2023,2,7);
	d1 = d2;
	d1.show();
	return 0;
}

insert image description here
Note: 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.
But it is necessary for the space with space application, we can't just rely on the assignment operator overload generated by the compiler itself:

class A
{
    
    
public:
	A()
	{
    
    
		a = 10;
		arr = (int*)malloc(sizeof(int) * a);
		for (int i = 0; i < a; i++)
		{
    
    
			arr[i] = i;
		}
	}
	void show()
	{
    
    
		for (int i = 0; i < a; i++)
		{
    
    
			cout << arr[i] << " ";
		}
		cout << endl;
	}
 
	int* arr;
	int a;
};
int main()
{
    
    
	A a;
	A b;
	a.show();
	b.show();
	b = a;
	//修改b对象的arr数组
	for (int i = 0; i < b.a; i++)
	{
    
    
		b.arr[i] = 0;
	}
	//观察两个对象中arr的元素
	b.show();
	a.show();
	return 0;
}

insert image description here
We just modified the arr array of the b object, but the arr array of the a object was also modified accordingly.

The specific situation is:
insert image description here

Seven. Pre-++ and post-++ overloading

We know that the only difference between pre-++ and post-++ is that the return value is different. Pre-++ returns the result after ++, and pre-++ returns the result after ++. Then after the operator is overloaded, it should also support the features of pre-++ and post-++.

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++:返回+1之后的结果
	// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
	Date& operator++()
	{
    
    
		_day += 1;
		return *this;
	}
	// 后置++:
	// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
	// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1
	// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
	Date operator++(int)
	{
    
    
		Date temp(*this);
		_day += 1;
		return temp;
	}
	void show()
	{
    
    
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
 
int main()
{
    
    
	Date d1(2022, 10, 1);
	d1.show();
	Date d2 = d1++;
	d2.show();
	d1.show();
 
	d2 = ++d1;
	d1.show();
	d2.show();
 
	return 0;
}

insert image description here

Eight. const members

The const-modified "member function" is called a const member function. The const-modified class member function actually modifies the implicit this pointer of the member function, indicating that any member of the class cannot be modified in the member function.

insert image description here

class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void ConstPrint() 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(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.ConstPrint();
	return 0;
}

insert image description here

Consider the following questions:
1. Can a const object call a non-const member function?
Answer: A const object cannot call a non-const member function, because the this of a non-const member function is not modified by const. If a const member function is called, it is equivalent to amplified permissions.

2. Can a non-const object call a const member function?
Answer: Non-const objects can call const member functions, because member functions modified by const actually modify this, which is equivalent to narrowing permissions and is allowed.

3. Can const member functions call other non-const member functions?
Answer: const member functions cannot call other non-const member functions, which is also an example of privilege amplification.

4. Can other const member functions be called in a non-const member function?
Answer: Non-const member functions can call other const member functions, which is also an example of privilege reduction.

Nine. Address and const address operator overloading

incomplete

10. cout output class information

incomplete

Eleven.Date class implementation

incomplete

Guess you like

Origin blog.csdn.net/weixin_47952981/article/details/129071677