C++ study notes (4) - classes and objects (middle)

content

1. The 6 default member functions of the class

2. Constructor

2.1 Concept

2.2 Features

3. Destructor

3.1 Concept

3.2 Features

4. Copy constructor

4.1 Concept

4.2 Features

5. Assignment operator overloading

5.1 Operator overloading

5.1 Assignment operator overloading

6. const members

6.1 Member functions of const-modified classes

6.2 Address and const address operator overloading

 7. The realization of the date class (the application of the above knowledge can be practiced)


1. The 6 default member functions of the class

If a class has no members at all, it is simply called an empty class, and there is nothing in an empty class? No, any class will automatically generate the following 6 default member functions if we don't write it.

class Date{};

2. Constructor

2.1 Concept

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.

Use , guarantees that each data member has a suitable initial value , and is called only once during the lifetime of the object .

2.2 Features

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

Its characteristics are as follows:

  • The function name is the same as the class name
  • no return value
  • The object instantiation compiler automatically calls the corresponding constructor
  • Constructor can be overloaded
class Date
{
public:
	// 构造函数
	// 1.无参构造函数
	Date()
	{

	}
	// 2. 有参构造
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 3.全缺省
	//Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //
private:
	int _year;
	int _month;
	int _day;
};
void test(){
  Date d1;//调用无参构造函数
  Date d2(2022,1,18);//调用带参的构造函数
}

Note : If the class does not explicitly define a constructor, the C++ compiler will automatically generate a no-argument default constructor, and once the user explicitly defines it, the compiler will no longer automatically generate it.

There are three default constructors:

  • The constructor generated by the compiler by default (will not handle built-in types, or random values; for custom types, they will call their own constructor initialization)
  • full default constructor
  • no-argument constructor

When we do not implement the constructor, the compiler will call the constructor generated by the compiler by default. Looking at the following code, can the default constructor of the compiler implement the initialization of member variables? 

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

 
 

The three values ​​are all random values. The default constructor of the compiler does not initialize the member variables. Does it mean that this function is useless?

Answer: C++ divides types into built-in types (primitive types) and custom types. The built-in type is the type that has been defined by the grammar: such as int/char..., the custom type is the type that we define by using class/struct/union. If you look at the following program, you will find that the compiler generates a default constructor 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
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

 Summary: 1. The default constructor built-in types (int/char/double) will not handle, or random values; for custom types (struct/class), they will call their own constructor initialization.

2. If there is no explicitly defined constructor in the class, the C++ compiler will automatically generate a parameterless default constructor, and once the user explicitly defines it, the compiler will no longer generate it.

3. In general, it is recommended to write a full default constructor, which can adapt to most scenarios.

3. Destructor

3.1 Concept

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

3.2 Features

Destructors are special member functions

Its characteristics are as follows:

  • The destructor name is preceded by the class name with the character ~
  • No parameters, no return value
  • A class has one and only one destructor. If there is no explicit definition, the system automatically generates a destructor.
  • At the end of the object declaration cycle, the C++ compilation system will automatically call the destructor.
typedef int DataType;
class SeqList
{
public :
SeqList (int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()//析构函数
{
if (_pData)
{
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
class String
{
public:
String(const char* str = "jack")
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
class Person
{
private:
String _name;
int _age;
};
int main()
{
Person p;
return 0;
}
//同样对于默认析构函数:对于内置类型成员不做处理,对于自定义类型会去调用他的析构函数

If we do not implement a destructor, the compiler will automatically generate a destructor, and the built-in type of the destructor heap will not be processed, and its destructor will be called for a custom type, which is the same as the default constructor generated by the compiler. The functions are somewhat similar.

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

class B
{
public:
	B()
	{
		cout << "B()" << endl;
	}

	~B()
	{
		cout << "~B()" << endl;
	}
private:
	int _b;
};

int main()
{
	A a;
	B b;

	return 0;
}
// 因为对象是定义在函数中,函数调用会建立栈帧,
	// 栈帧中的对象构造和析构也要很符合后进先出。
	// 所以这里的顺序是: A()->B()->~B()->~A()

 

4. Copy constructor

4.1 Concept

When creating an object, is it possible to create a new object that is the same as an object?
Constructor: There is only a single parameter, which is a reference to an object of this class type (usually const modified), which is automatically called by the compiler when a new object is created with an existing class type object

4.2 Features

  •  The copy constructor is an overloaded form of the constructor;

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

class Date {
public:
	Date(int year = 2020, int month = 2, int day = 20)
	{
		_year = year;
		_month = month;
		_day = day;
	}
  // Date(Date d)
//若使用传值拷贝,则在传参时会先调用拷贝构造函数(因为传值拷贝时需先将原数据拷贝到临时空间再使用临时空间中的数据进行赋值),这样又会出现拷贝,这样就会陷入循环往复过程。   
   {
      _year=d._year;
      _month=d._month;
      _day=d._day;
     }//
	Date(const Date& d)//这里传参时也推荐加 const 因为被拷贝的对象是不应该被修改的
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day  << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 总结:
// 1.Date这样的类,需要的就是浅拷贝,那么默认生成的拷贝构造就够用了,不需要自己写
// 2.但是像Stack这样的类,需要的是深拷贝,浅拷贝会导致析构两次,程序就会崩溃等问题

The reason why passing by value causes infinite recursion: 

  •  If no definition is displayed, the system generates a default copy constructor . The default copy constructor object is copied in byte order according to the memory storage. This copy is called shallow copy, or value copy.

For a custom type (Stack, etc.), the compiler will still perform a shallow copy on it, which will copy the address of the original object's open space. The consequences of this are:
1. Two objects share a space, and when the analysis is called The space will be released twice during the constructor ;
2. Inserting and deleting data in one of the objects will cause another object to also insert and delete data .
Therefore, for a class like Stack, the copy structure generated by the compiler by default is a shallow copy, which does not meet the requirements and needs to implement a deep copy .

class String
{
public:
	String(const char* str = "jack")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
	char* _str;
};
int main()
{
	String s1("hello");
	String s2(s1);
}

 The code crashes directly, this is the consequence of shallow copy: the same memory is destructed twice 

5. Assignment operator overloading

5.1 Operator overloading

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name , as well as its return value type, function name and parameter list. Its 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)

Notice:

  • You cannot create new operators by concatenating other symbols: such as operator@
  • An overloaded operator must have an operand of class type or enumeration type
  • Operators used for built-in types, whose meaning cannot be changed, for example: 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
  • * , :: , sizeof , ?: , . Note that the above five operators cannot be overloaded. This often occurs in multiple-choice questions in written exams.
     

Code example: '>'

class Date
{
public:
    Date(int year = 2022, int month = 2, int day = 20)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    
    // bool operator>(Date* this, const Date& d2)
 
    bool operator>(const Date& d2)
    {
        
    if (_year > d2._year)
	{
		return true;
	}
	else  if(_year == d2._year && _month > d2._month)
	{
		return true;
	}
	else if (_year == d2._year && _month == d2._month && _day > d2._day)
	{
		return true;
	}
	else {
		return false;
	}
    }
private:
    int _year;
    int _month;
    int _day;
};
void Test ()
{
    Date d1(2021, 1, 1);
    Date d2(2021, 1, 2);
    cout<<(d1 >d2)<<endl;// <<优先级高于==,这里需加括号
    //等于d1.operator>(d2)
}

5.1 Assignment operator overloading

The assignment operator mainly has the following five points:

  • Parameter Type
  • return value
  •  Check if you assign yourself a value
  •  return *this
  • If a class does not explicitly define an assignment operator overload, the compiler will also generate one to complete the endian value copy of the object.
class Date
{
public:
	Date(int year = 2000, int month = 9, int day = 18)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 赋值运算符重载也是一个默认成员函数,也就是说我们不写编译器会自动生成
	// 编译器默认生成的赋值运算符重载跟拷贝构造的特性是一样的
	// 内置类型,会完成浅拷贝,自定义类型,会调用他的赋值运算符重载完成拷贝
	Date& operator=(const Date& d) // 有返回值是为了解决连续赋值的情况
	{
		if (this != &d)          //不是自己给自己赋值才需要拷贝
		{
			_year = d._year;            // 赋值运算符重载也是拷贝行为,不一样的是,
			_month = d._month;          // 拷贝构造是创建一个对象时,将同类对象初始化的拷贝。
			_day = d._day;              // 这里的赋值拷贝时两个对象都已经存在了,都被初始化过了
		}
		return *this;
	}
 
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
 
private:
	int _year;
	int _month;
	int _day;
};
 
 
int main()
{
	Date d1;
    Date d2(2022,2,20);
    d1=d2;//这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的
 
	Date d3;
	d3 = d2 = d1;//连续赋值
	d2.Print();
	d3.Print();
 
	Date d4 = d1; // 拷贝构造
	return 0;     // 拷贝构造:拿一个已经存在的对象去拷贝初始化另一个要创建的对象
}                 // 赋值重载:两个已经存在的对象拷贝
            

Difference between assignment operator overloading and copy constructor

The same: when there is no explicit definition, the compiler will generate one by default. For built-in types, it will perform a shallow copy in byte order, and for custom types, it will call its own copy constructor or operator=.
Difference: copy construction: Initialize an object that is about to be created with an existing object .
           Assignment overloading: Two copies of existing objects .

6. const members

6.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 () 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可以调用const;const不能调用非const(权限不能放大)
const Date d2;//建议成员函数中,不需要改变成员变量,都加上const。
d2.Display ();
}

6.2 Address and const address operator overloading

These two operators generally do not need to be overloaded. You can use the overload of the default address fetching generated by the compiler. Only in special cases, overloading is required, such as letting others get the specified content .

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

 7. The realization of the date class (the application of the above knowledge can be practiced)

Date.h

Date.h
#pragma once
#include<iostream>
using namespace std;
class Date {
public:
	Date(int year = 1, int month = 1, int day = 1);
	void print();
	int GetMonthDay(int year,int  month);//获取每个月的天数
	bool operator>(const Date& d);//比较第一个数是否比第二个数大
	bool operator<(const Date& d);//以下同理
	bool operator>=(const Date& d);
	bool operator<=(const Date& d);
	bool operator==(const Date& d);
	bool operator!=(const Date& d);
	//d1+=100
	Date& operator+=(int day);//在一个数本身加上天数,本身改变
	//d1+100
	Date operator+(int day);//一个数加上给定的天数是哪天?本身不改变
	//d1-=100
	Date& operator-=(int day);
    //d1-100
	Date operator-(int day);
	Date operator--(int);
	Date& operator--();
	Date operator++(int);//后置++,为了区分前置++与其构成函数重载,需要加int(规定,不允许是其他类型)
	Date& operator++();//前置++
	int operator-(const Date& d);//两个日期相减
	void PrintWeekDay() const;//打印今天是星期几

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

Date.c 

#include"Date.h"
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if(!(_year >= 0 && (_month > 0 && _month < 13) && (_day>0 && _day <= GetMonthDay(year, month))))
	{
		cout << "非法日期" << endl;
	}
}
void Date::print()
{
	cout << _year << "-" << _month << "-" << _day << endl; 
}
int Date::GetMonthDay(int year, int month)
{
	int MonthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = MonthDayArray[month];
	if (month == 2 && (year % 4 == 0 && year % 400 != 0) || year % 100 == 0)
	{
		day += 1;
	}
	return day;
}
bool Date::operator>(const Date& d)
{
	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;
	}
}
bool Date::operator==(const Date& d)
{
	if (_year == d._year && _month == d._month && _day == d._day)
	{
		return true;
	}
	else {
		return false;
	}
}
bool Date::operator>=(const Date& d)
{
	return (*this > d || *this == d);
}
bool Date::operator<(const Date& d)
{
	return !(*this >= d);
}
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}
Date& Date::operator+=(int day)
{
	//2021-12-28 +=300
	_day += day;
	while ( _day> GetMonthDay(_year, _month))
	{
		_month += 1;
		_day -= GetMonthDay(_year, _month);
		while (_month == 13)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)
{
	Date ret(*this);
	ret += day;
	return ret;
}
Date& Date::operator-=(int day)
{
	//2021-1-20 -45
	_day -= day;
   while(_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
   return *this;
}
Date Date::operator - (int day)
{
	Date ret(*this);
	ret -= day;
	return ret;
}
//前置++
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
Date Date::operator++(int)
{
	Date ret(*this);
	*this += 1;
	return ret;
}
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
Date Date::operator--(int)
{
	Date ret(*this);
	*this -= 1;
	return ret;
}
int Date::operator-(const Date& d)
{
	Date max = *this;
     Date min = d;
	 int flag = 1;
	 if (*this < d)
	 {
		 max = d;
		 min = *this;
		 flag = -1;
	 }
	 int count = 0;
	 while (min != max)
	 {
		 min++;
		 count++;
	 }
	 return flag * count;
}
void Date::PrintWeekDay() const
{
	const char* arr[] = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天" };
	Date start(1900, 1, 1);
	int count = *this - start;
	cout << arr[count % 7] << endl;
}

 The above is part of the content of classes and objects. If there are any shortcomings or better insights into the code, please leave a message in the comment area to discuss and make progress together!

 

Guess you like

Origin blog.csdn.net/m0_58367586/article/details/123019812
Recommended