类和对象(中)

目录

一.默认成员函数

二.构造函数

三.析构函数

四.拷贝构造函数

五.赋值运算符重载

什么是运算符重载?

赋值运算符重载

六.取地址运算符重载

附件:日期类的实现


一.默认成员函数

在类中,有我们自己实现的成员函数。但是,还有一些“隐而不现”的成员函数,由编译器自己生成,称为默认成员函数。顾名思义,用户无须实现,编译器自动生成。

那么,这些隐而不现的函数是什么,有什么功能呢?

在C++98的标准下,默认生成了6个成员函数:

C++11增加了移动构造和移动赋值两个成员函数(不作讲解)。

我们要明确两点:1.我们不显示实现,这些默认生成的函数行为是什么?

                              2.我们显示实现后,编译器就不会自动生成了,那该如何实现?

二.构造函数

功能:构造函数,完成的是在用类实例化出对象时完成对象的初始化工作

用代码来理解:

typedef int STDataType;
class Stack
{
public:
    //Init
	void Init(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = 0;
		_capacity = 0;
	}
    
    //构造函数
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType)*n);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = 0;
		_capacity = 0;
	}
private:
	STDataType* _a;
	int _top;
	int _capacity;
};

int main( )

{

        //手动调用Init函数初始化

        Stack st1;

        st1.Init();

        

        //自动调用构造函数初始化

        //Stack st2;
        return 0;

}

 构造函数的功能相当于代替了Init函数,它的便捷性在于,在创建对象时自动调用。因此,以后便不在需要实现Init函数,转而实现构造函数。

构造函数的特点:

1.函数名:与类名相同

2.无返回值(注意:不是void)

3.可以构成函数重载

4.对象实例化时,自动调用

认识三个默认构造函数(零实参构造函数):

无参构造,全缺省构造,编译器自动生成的构造

我们在对象实例化的过程中,构造函数的调用是自动的,如果我们不传实参,就能调用的构造函数,称为默认构造函数。 

Date类样例:

class Date
{
public:
	//无参构造
	Date()
	{
		_year = 2024;
		_month = 1;
		_day = 1;
	}

	//带参构造
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//全缺省构造
	//Date(int year = 2024, int month = 1, int day = 1)
	//{
	//	_year = year;
	//  _month = month;
	//	_day = day;
	//}
	private:
	int _year;
	int _month;
	int _day;
};

int main( )

{

        //假设Date类中,我们实现了一个无参构造和代参构造

        Date d1;

        Date d2(2000,1,1);

 }

 由于d1要自动调用构造函数,因此我们不传实参时,它会去找到它的默认构造函数(无参构造)。

int main( )

{

        //假设Date类中,我们实现了一个全缺省构造

        Date d1;

        Date d2(2000,1,1);

 }

上述d1去调用类中的全缺省构造,由于我们不传实参,d1会使用缺省值。

注意,全缺省构造和无参构造不能同时存在,否则会产生调用歧义。

可以认为,全缺省构造 = 无参构造 + 带参构造

用户不显示实现构造函数,编译器默认生成的构造函数的行为

1.对于内置类型的初始化没有要求。

2.对于自定义类型,调用这个成员变量的默认构造函数。

自定义类型:语言提供的原生数据类型,如char/int/double/指针等。

内置类型:我们使用class/struct定义的类类型。

三.析构函数

功能:与构造函数功能相反,完成对象资源的清理和释放工作。

析构函数的特点:

1.函数名:~类名,如~Date

2.无参,无返回值

3.一个类只能有一个析构函数

4.对象生命周期结束时,自动调用析构函数

5.一个局部域的多个对象,C++规定后定义的先析构

编译器默认生成的析构函数的行为

1.对于内置类型,不作处理。

2.对于自定义类型,自动调用成员变量的析构函数。需要注意的是,自定义类型无论什么情况都会调用它的析构函数。

四.拷贝构造函数

功能:实例化对象时,用已存在的对象拷贝构造新对象。

拷贝构造函数的特点:

1.函数名与类名相同

2.第一个参数必须是当前类类型的引用

3.无返回值

4.C++规定,自定义类型的传值传参和传值返回,都会去调用拷贝构造

 编译器默认生成的拷贝构造函数 的行为:

1.对于内置类型成员变量会完成值拷贝/浅拷贝

2.对于自定义类型的成员变量,会去调用它的拷贝构造

五.赋值运算符重载

什么是运算符重载?

C++语言允许通过运算符重载的形式,给运算符定义新的含义,使得运算符能够适用于自定义类型。

运算符重载本质上是一个函数,函数名有特别的规定:operator运算符,例如:operator+,operator-。函数参数必须包含至少一个自定义类型的参数。

对于二元运算符,运算符左侧的操作数默认作为第一个参数 ,右侧操作数作为第二个参数。如果重载为成员函数,参数个数比运算符操作数少一个;左侧操作数传入隐藏的this指针,右侧操作数传入(第一个)形参位置。

前置++/--与后置++/--的区分:以++为例,重载前置++和后置++时,函数名都是operator++,无法做出很好的区分。于是,C++规定,重载后置++时,参数列表多一个int类型的形式参数

重载<< 和 >> 时,如果重载成成员函数,隐藏的this指针占据了左操作数的位置,会出现 对象<<cout 或 对象 >> cin 的用法,不符合习惯。所以,上述运算符需要重载为全局函数,把ostream/istream放到第一个形参的位置。

五个无法重载的运算符:.*      ::       sizeof       ?:         .

赋值运算符重载

功能:用于两个已存在对象的直接拷贝赋值。

赋值运算符重载的特点:

1.函数名:operator=

2.参数: 建议写成const当前类类型的引用,减少拷贝构造

3.返回类型:建议写成当前类类型的引用,减少拷贝构造

编译器默认生成的赋值运算符重载函数的行为:

1.对于内置类型,完成值拷贝/浅拷贝赋值

2.对于自定义类型,调用自定义类型成员变量的赋值运算符重载

 

六.取地址运算符重载

功能:不让别人取到当前类对象的(真实)地址,返回一个任意地址 

分为const取地址运算符重载和普通取地址运算符重载。

一般无需自己实现,使用编译器默认生成的即可。

扩展:

const 成员函数,cosnt修饰的是隐藏的this指针,因此在const成员函数内,无法修改当前类类型的成员变量。

附件:日期类的实现

功能:实现日期加减天数,日期-日期等功能

Date.h

#pragma once
#include <iostream>
using namespace std;

class Date
{
	//友元声明
	//流插入<<
	friend ostream& operator<<(ostream& out, const Date& d);
	//流提取
	friend istream& operator>>(istream& in, Date& d);

public:
	//类中定义的成员函数,默认inline
	int GetMonthDay(int year, int month)
	{
		static int monthDayArray[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;
		}
		return monthDayArray[month];
	}

	bool CheckDay(int year, int month, int day)
	{
		if (month <= 0 || month >= 13 || day > GetMonthDay(year, month) || day <= 0)
			return false;

		return true;
	}

	//构造函数
	Date(int year = 2024, int month = 1, int day = 1);
	
	//逻辑运算符
	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);


	//算术运算符
	Date& operator+=(int day);
	Date& operator-=(int day);
	Date operator+(int day);
	Date operator-(int day);

	Date operator++(int);//后置++
	Date& operator++();//前置++
	Date operator--(int);//后置--
	Date& operator--();//前置--

	//d1 - d2
	int operator-(const Date& d);
private:
	int _year;
	int _month;
	int _day;
};
//流插入<<
ostream& operator<<(ostream& out, const Date& d);
//流提取>>
istream& operator>>(istream& in, Date& d);

Date.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	
	//日期合法性
	if (!CheckDay(year, month, day))
	{
		cout << "非法日期:" << *this;
	}
}





//逻辑运算符
bool Date::operator==(const Date& d)
{
	if (_year == d._year)
	{
		if (_month == d._month)
		{
			return _day == d._day;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
}

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

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)
{
	if (_year > d._year)
	{
		return  true;
	}
	else if (_year == d._year)
	{
		if (_month > d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			return _day > d._day;
		}
	}

	return false;
}

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




//算术运算符
Date& Date::operator+=(int day)
{
	if (day < 0)
		*this -= (-day);

	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_month = 1;
			_year++;
		}
	}
	return *this;
}

Date& Date::operator-=(int day)
{
	if (day < 0)
		*this += (-day);

	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

Date Date::operator+(int day)
{
	Date tmp = *this;
	tmp += day;
	return tmp;
}

Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

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

Date Date::operator--(int)
{
	Date tmp = *this;
	*this -= 1;
	return tmp;
}

Date& Date::operator--()
{
	*this -= 1;
	return *this;
}


//日期-日期
int Date::operator-(const Date& d)
{
	int cnt = 0;
	Date greater = *this;
	Date less = d;
	int flag = 1;

	if (greater < less)
	{
		greater = d;
		less = *this;
		flag = -1;
	}

	while (less != greater)
	{
		++less;
		cnt++;
	}

	return cnt * flag;
}




//流插入<<
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
//流提取>>
istream& operator>>(istream& in, Date& d)
{
	while (1)
	{
		cout << "请输入年月日:";
		in >> d._year >> d._month >> d._day;

		if (!d.CheckDay(d._year, d._month, d._day))
		{
			cout << "日期非法,请重新输入!!!" << endl;
		}
		else
		{
			break;
		}
	}

	return in;
}

 

今天的内容就到这里,感谢大家的支持 !!!

上一篇:springboot074智能物流管理系统(论文+源码)_kaic-5系统详细实现


下一篇:SQL CHECK 约束:确保数据完整性的关键