C++ classes and objects (medium)

content

6 default member functions of a class

1. Constructor

2. Destructor function

Third, the copy constructor

4. Overloading of assignment operators

4.1 Operator overloading

4.2 Overloading of assignment operators

Five, const members

5.1 Member functions of const-modified classes

Six, address and const address operator overloading


6 default member functions of a class

  If a class has no members, it is referred to as an empty class. Nothing in the empty class? No, any class will automatically generate the following 6 default member functions if we don't write it
class Date {}; //空类

Note: These are 6 special member functions

1. Constructor

Concept: Although the name of the constructor is called construction, it does not open space to create objects, but initializes objects

Features are as follows:

1. The function name and the class name are the same

2, no return value, not void

3. When the object is instantiated, the corresponding constructor is automatically called

4. Constructors can be overloaded

class Date
{
public:
	Date()//1、无参构造函数
	{
		_year = 2000;
		_mouth = 2;
		_day = 5;
	}
	Date(int year, int mouth,int day)//2、带参构造函数(可以是缺省的参数)
	{
		_year = year;
		_mouth = mouth;
		_day = day;
	}
//以上两个函数名字相同,但是参数不同,构成了函数重载

private:
	int _year;
	int _mouth;
	int _day;
};
int main()
{
	Date d1;//这里就是具体的对象实例化
	Date d2(2022,2,12);
	return 0;
}

5. Tips: C++ divides our types into two categories

The first type of built-in type: int/double/pointer type/array of built-in types, etc.

The second type of custom type: the type defined by struct/class

Special handling rules:

① We do not write the default constructor generated by the compiler, and do not deal with built-in types

②For a member variable of a custom type, its default constructor (that is, a member function that can be called without parameters) will be called for initialization

③ If there is no default constructor, the compiler will report an error

There are three types of default constructors (which can be called without parameters): all default, no parameters, we do not write the default generated by the compiler

for example:

//A类
class A
{
public:
	A()
	{
		cout << "调用A()" << endl;
		_a = 0;
	}
	//如果是下面的带参的函数,就不是默认构造函数,编译器就会报错
	/*A(int a)
	{
		cout << "调用A()" << endl;
		_a = 0;
	}*/
private:
	int _a;
};

class Date
{
public:
	//这里不写
private:
	int _year;
	int _mouth;
	int _day;
	A _aa;//这里对于自定义类型,会去调用A类型的默认构造函数
};
int main()
{
	Date d1;
	return 0;
}

operation result: 

Note: No processing is performed for built-in types. For the _aa member variable in the custom type A, go back and call the default constructor of class A. If there is no default constructor, an error will be reported.

Open the watch window and observe:

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

E.g: 

class Date
{
public:
	//这里我们不手动写,编译器会默认生成构造函数帮我们完成初始化操作
private:
	int _year;
	int _mouth;
	int _day;
};
int main()
{
	Date d1;
	return 0;
}

We open the monitoring window at this time, and we can observe that the random value initialized to the object also confirms our conclusion above:

requires attention:

class Date
{
public:
	Date()//1、无参构造函数
	{
		_year = 2000;
		_mouth = 2;
		_day = 5;
	}
	Date(int year=2000, int mouth=2, int day=5)//2、带参构造函数
	{
		_year = year;
		_mouth = mouth;
		_day = day;
	}

private:
	int _year;
	int _mouth;
	int _day;
};
int main()
{
	Date d1;//这里就会报错
	//1、语法上无参和全缺省的可以同时存在
	//2、但是如果存在无参调用,就会存在歧义(编译器报错)
	Date d2(2022, 2, 12);
	return 0;
}

2. Destructor function

Concept: Contrary to the function of the constructor, the destructor does not complete the destruction of the object, and the local object destruction is done by the compiler. When the object is destroyed, the destructor will be automatically called to complete the cleanup of some resources in the object.

For example: some pointers opened up by our dynamic memory, we need to release them, which is called resource cleanup

Features :

1. The destructor name and the class name are the same, and a ~ is added in front

2, no parameters, no return value

3. A class has one and only one destructor. If it is not explicitly defined, the system will automatically generate a default destructor

4. At the end of the object life cycle, the C++ compilation system will automatically call the destructor

class Date
{
public:
	~Date()
	{//Date类没有资源需要清理,所以Date不实现析构函数也是可以的
		cout << "调用 ~Date()" << endl;
	}
private:
	int _year;
	int _mouth;
	int _day;
};
int main()
{
	Date d1;
	return 0;
}

But we need to clean up resources in some cases: (for example , malloc resources need to be processed )

class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
	}
	~stack()
	{
		free(_a);
		_a = nullptr;
        _top = _capacity = 0;
	}
private:
	int* _a;
	size_t _top;
	size_t _capacity;
	
};
int main()
{
	stack s1;
	stack s2(10);
	return 0;
}

5、tips:

If we don't write it, the default generated destructor is similar to the constructor

①For member variables of built-in types, no processing is performed

②For a member variable of a custom type, its destructor will be called

Let's take a question here, using two stacks to implement the queue, in which the default generated constructor and destructor will call its constructor and destructor:

class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
	}
	~stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	size_t _top;
	size_t _capacity;
	
};

//两个栈实现一个队列
class MyQueue
{
public:
	// 默认生成构造函数和析构函数会对自定义类型成员调用他的构造和析构
	void push(int x)
	{}
private:
	stack pushST;
	stack popST;
};
int main()
{
	MyQueue q;
	return 0;
}

Third, the copy constructor

Concept: As the name suggests, it is to copy and assign an object that is exactly the same as itself. The copy constructor has only a single formal parameter , and the modified parameter is a reference to the object of this class type (usually const modified) , which is automatically called by the compiler when creating a new object with an existing class type object.

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, 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;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2000, 1, 1);
	Date d2(d1); // 用已存在的对象d1创建对象d2
//用一个同类型对象初始化就是拷贝构造,将d1的内容复制给d2

	return 0;
}

Features :

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

2. There is only one function parameter in the copy construction , and the parameter must be passed by reference . The method of passing by value will cause infinite recursive calls.

class Date
{
public:
	Date(int year = 1, 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()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	//将d1的内容复制给d2
	//用同一个类型的对象去初始化,就是拷贝构造
	return 0;
}

Why is it passed by reference here?

date is an alias of d1 to avoid infinite recursion caused by copying when passing values

Why is it recommended to add const here?

Here we want to copy an object that is the same as the original object, we want to keep the content of the original object unchanged, so let the original object have const attributes

3. If not explicitly defined, the system generates a default copy constructor

Default generated copy constructor:

1) Member variables of built-in types: byte-order copy (shallow copy) will be completed

2) Member variables of custom types: its copy constructor will be called

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//我们这里不写拷贝构造函数,编译器会自动生成
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	//将d1的内容复制给d2
	return 0;
}

Open the watch window to observe two objects:

It can be found that a shallow copy (value copy) occurs, and a d2 with exactly the same content is copied.

We do not define a copy constructor here, but the copy constructor automatically generated by the compiler still completes the object copying task.

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

Sometimes, if we don't write it, the copy constructor automatically generated by the compiler is enough, but the copy constructor automatically generated by the compiler cannot complete a deep copy. For example, when we need to copy and construct a stack, for the default The generated copy constructor cannot be used

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_ps = (int*)malloc(sizeof(int)* capacity);
		_size = 0;
		_capacity = capacity;
	}
	void Print()
	{
		cout << _ps << endl;// 打印栈空间地址
	}
private:
	int* _ps;
	int _size;
	int _capacity;
};
int main()
{
	Stack stack1;
	stack1.Print();// 打印stack1栈空间的地址
	Stack stack2(stack1);// 用已存在的对象stack1创建对象stack2
	stack2.Print();// 打印s2栈空间的地址
	return 0;
}

Analysis: If our copy structure is still a shallow copy in byte order, the copy will copy the address of the open stack 1 to the stack 2, so that the two pointers will point to the same area of ​​the heap, so use the The simultaneous operation of the two stacks will affect each other. When stack2 is destructed, stack1 also destructs the same area, which is not allowed and will cause the program to crash . It is correct that we write the copy structure of the stack by ourselves. When the program finishes running, the stack2 stack will be destructed first. At this time, the stack space will be released, and then the stack1 stack will also be destructed, and the space will be released again. .

4. Overloading of assignment operators

4.1 Operator overloading

By default, C++ does not support the use of operators for objects of custom types. In order to allow them to operate (such as size), operator overloading is proposed. Operator overloading is a function with a special function name . The purpose is to allow custom types to be able to Like built-in types can be manipulated directly using operators

Features :

1. It has its return value type, function name and parameter list. Its return value type and parameter list are similar to ordinary functions.

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

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

4. Parameters: The operator has several operands, and it has several parameters

Note :

1) You cannot create new operators by connecting other symbols: such as operator@

2) An overloaded operator must have an operand of a class type or an enumeration type

3) The meaning of operators for built-in types cannot be changed. For example, the built-in integer + cannot change its meaning.

When an overloaded function is a class member, its formal parameters appear to be one less than the number of operands. The operator of the member function has a default parameter this, which is limited to the first parameter

4) .* , :: , sizeof , ?: , . Note that the above 5 operators cannot be overloaded. This often occurs in multiple-choice questions in written exams.

We want to implement operator overloading, let's first take a look at the global operator>

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
//private:     //这里需要放开成员变量,会破坏程序的封装性
	int _year;
	int _month;
	int _day;
};
bool operator>(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
		return true;
	else if (d1._year == d2._year && d1._month > d2._month)
		return true;
	else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day)
		return  true;
	else
		return false;
}

int main()
{
	Date d1(2022,2,1);
	Date d2(d1);
	Date d3(2000, 1, 1);
	
	cout <<(d1 > d3 )<< endl;
	cout << (operator>(d1,d3)) << endl;
	//将d1的内容复制给d2
	return 0;
}

Here, it will be found that the operator overloading into a global requires the public member variables, which will destroy the encapsulation of the program.

In fact, we can write overloading in the class:

#include<iostream>
using namespace std;
//重载成员函数
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator>(const Date& d)//成员函数默认有一个隐藏的this指针
	{
		if (_year > d._year)
			return true;
		else if (_year == d._year && _month > d._month)
			return true;
		else if (_year == d._year && _month == d._month && _day > d._day)
			return  true;
		else
			return false;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2022, 2, 1);
	Date d2(d1);
	Date d3(2000, 1, 1);
	cout << (d1>d3) << endl;//这样写会被编译器转化为下面这行
	cout<< d1.operator>(d3)<<endl;
	return 0;
}

Member functions will have a hidden this pointer

4.2 Overloading of assignment operators

we know:

An existing object to initialize an object that immediately creates a city - copy construction

Assignment operation between two existing objects - assignment copy

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& d)//成员函数默认有一个隐藏的this指针,加const保持赋值的那个对象不变,具有常属性
	{
		if (this != &d)//预防自己给自己拷贝,没有这个必要
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;//引用返回,减少值拷贝
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2022, 2, 1);
	Date d2(d1);
	Date d3(2000, 1, 1);

	d1 = d3;
//将d3的内容拷贝给d1,将d1的内容给覆盖了
	return 0;
}

The main features of the assignment operator :

1. The parameter type is a reference and is modified with const

 The right operand is an existing object, we generally do not change it, so add a const modification

2. Use reference to return, in order to reduce multiple copies

If we assign d2=d1, there is no need to return a value at all, but if we assign d3=d2=d1 continuously, the value returned to d2 needs to be copied, and then assigned to d3, so that a copy of the return call is made at the end. This forms multiple copies. In order to avoid unnecessary copies, we use reference return

3. Check whether you assign yourself a value

If there is no unnecessary assignment such as d1=d1, we can avoid unnecessary assignment operations

4、return *this

Out of the scope *this still exists, *this is the d object (the object is not destroyed), use the reference to return to reduce the copy

5. If there is no explicit definition of assignment operator overloading in a class, the compiler will generate one by default, which will complete the byte order copy of the object 

The compiler generates assignment overloads by default, and the root copy constructor does something similar

1) For built-in types, a shallow copy in endianness is done

2) For custom types, it will call its own operator overloading

Five, const members

5.1 Member functions of const-modified classes

The const-modified class member function is called a const member function, and the const-modified class member function actually modifies the implicit this pointer of the member function, indicating that no member of the class can be modified in the member function.

class Date
{ 
public :
 void Display ()
 {
 cout<<"Display ()" <<endl;
 cout<<"year:" <<_year<< endl;
 cout<<"month:" <<_month<< endl;
 cout<<"day:" <<_day<< endl<<endl ;
 }
 void Display () const
 {
 cout<<"Display () const" <<endl;
 cout<<"year:" <<_year<< endl;
 cout<<"month:" <<_month<< endl;
 cout<<"day:" <<_day<< endl<<endl;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};
void Test ()
{
 Date d1 ;//普通对象
 d1.Display ();
 
 const Date d2;//const修饰对象
 d2.Display ();
}

Conclusion : It is good to add const to member functions. It is recommended to add all that can be added. In this way, both ordinary objects and const-modified objects can be called, but if you want to modify member functions of member variables, you cannot add const

Six, address and const address operator overloading

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

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. You can use the overload of the default address generated by the compiler. Only in special cases, you need to overload, such as letting others get the specified content!

Thanks for watching!

Guess you like

Origin blog.csdn.net/weixin_57675461/article/details/122991851