《c++从0到99》 三 类和对象 中

本文详细介绍了C++类的六个默认成员函数:构造函数、拷贝构造函数、析构函数、赋值运算符重载、const修饰的成员函数,以及它们在实际编程中的应用。同时,通过日期类的实现,展示了如何利用这些函数进行对象的初始化、复制、清理和操作。重点讨论了构造函数的初始化列表、拷贝构造函数的作用、析构函数在资源管理中的角色、赋值运算符重载的注意事项以及const成员函数的使用规则。
摘要由CSDN通过智能技术生成

0. 类的六个默认构造函数

在这里插入图片描述
定义一个空类:

class Date
{

}

经过编译器处理之后,类Date不在为空,它会自动的生成六个默认的成员函数,即使这六个成员函数什么也不做。(这些成员函数当我们没有定义时,编译器会显示的调用)处理之后相当于:

class Date
{
public:
    Date();// 构造函数
    Date(const Date& date);// 拷贝构造函数
    ~Date();// 析构函数
    Date& operator=(const Date& date);// 赋值运算符重载
    Date* operator &();// 取地址运算符重载
    const Date* operator &() const;// const修饰的取地址运算符重载
}

1. 构造函数

概念:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象的时候,由编译器自动调用,在对象的生命周期内只调用一次,以保证每个数据成员都有一个合适的初始值。

特性

  • 在一个对象的生命周期内只能调用一次
  • 名字与类名相同
  • 没有返回值
  • 有初始化列表
  • 编译器自动调用
  • 构造函数可以重载,实参决定了调用哪个构造函数
  • 如果没有显示的调用时,编译器会自己提供一个默认的构造函数。
  • 无参构造函数和带有缺省值的构造函数都认为是缺省构造函数,并且缺省构造函数只能有一个
  • 构造函数不能用const修饰。

初始化链表
以冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
初始化顺序

  • 数据成员在类中定义的顺序就是参数列表中的初始化顺序;
  • 初始化列表仅用于初始化数据成员,并不指定这些数据成员的初始化顺序;
  • 每个成员在初始化列表中只能出现一次;
  • 尽量避免使用成员初始化成员,成员初始化顺序最好和成员的定义顺序保持一致
    必须要在初始化列表进行初始化的成员有:引用数据类型,const修饰的数据类型,类类型成员。

默认构造函数

默认构造函数使用与成员变量初始化相同规则初始化成员,对于内置类型和复合类型的成员,如组、指针,只对定义在全局定义作用于的对象初始化,对于局部作用域的内置和复合类型不作初始化。

explcit:

用explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复。

2. 拷贝构造函数

概念:只有单个形参,且该形参是本类对象的引用,创建对象时使用已存在的同类对象来进行初始化。这种类型的构造函数被称为拷贝构造函数。

class Date
{
 public:
 	Date(const Date& date)// 拷贝构造函数
 	{
 		_year = date._year;
 		_month = date._month;
 		_day = date._day;
 	}
 private:
 	int _year;
 	int _month;
 	int _day;
}

特征

  • 是构造函数的重载
  • 参数必须是本类对象的引用
  • 如果不显式定义,系统将生成一个默认的拷贝构造函数。

3. 析构函数

概念:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和善后工作。
特性

析构函数是特殊的成员函数。
其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
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;
};

4. 赋值运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型或者枚举类型的操作数
  • 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 作为类成员的重载函数时,其形参看起来比操作数数目少1,成员函数的操作符有一个默认的形参this,限定为第一个形参
  • .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。
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)
	{
		if(this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
private:
	int _year ;
	int _month ;
	int _day ;
};

主要注意:

  1. 参数类型
  2. 返回值
  3. 检测是否自己给自己赋值
  4. 返回*this
  5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝

5. const修饰的成员函数

将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
使用方法:直接在要修饰的成员函数后面加const。

class Date
{
 public:
 	void PrintDate()const// = void PrintDate(const Date* this)
 	{
 		cout<<_year<<"."<<_month<<"."<<_day<<endl;
 	}
 private:
 	int _year;
 	int _month;
 	int _day;
}

注意

  1. const对象不能调用非const成员函数。
    在这里插入图片描述
    因为const对象中的属性值不能发生改变,调用非const成员函数setA会导致对象的内容发生改变,编译器就会报错。const对象拥有只读权限,非const成员函数拥有可读可写权限,会将权限放大,不能调用。
  2. 非const对象可以调用const成员函数。
    在这里插入图片描述
    因为a对象中的内容可读也可以修改,而const修饰的成员函数只能读取数据,满足a对象的权限,所以可以调用。从a对象的可读可写到成员函数的只读,权限缩小,可以调用
  3. const成员函数内不能调用其它非const成员函数。
    在这里插入图片描述因为getA中的this只有只读权限,setA中this指针有可读可写权限,getA中的this指针满足不了setA中指针的全部权限。getA中this指针只读到setA中this指针可读可写,权限被放大,不能调用。
  4. 非const成员函数内可以调用其他const成员函数。
    在这里插入图片描述
    因为setA中的this指针有可读可写权限,getA中的this指针只有只读权限,setA中this指针拥有的权限可以满足getA中this指针的权限。setA中this指针可读可写到getA中this指针只读,权限缩小,可以调用。

6. 日期类的实现

class Date
{
public:
	bool CheckLeap(int year);
	// 获取某年某月的天数
	int GetMonthDay(int year, int month);
	// 打印年月日
	void PrintYearMonthDay()
	{
		printf("%d.%d.%d\n", _year, _month, _day);
	}

	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1);

	// 拷贝构造函数
	Date(const Date& d);

	// 赋值运算符重载
	Date& operator=(const Date& d);

	// 析构函数
	~Date();

	// 日期+=天数
	Date& operator+=(int day);

	// 日期+天数
	Date operator+(int day);

	// 日期-天数
	Date operator-(int day);

	// 日期-=天数
	Date& operator-=(int day);

	// 前置++
	Date& operator++();

	// 后置++
	Date operator++(int);

	// 后置--
	Date operator--(int);

	// 前置--
	Date& operator--();

	// >运算符重载
	bool operator>(const Date& d);

	// ==运算符重载
	bool operator==(const Date& d);

	// >=运算符重载
	inline bool operator >= (const Date& d);

	// <运算符重载
	bool operator < (const Date& d);

	// <=运算符重载
	bool operator <= (const Date& d);

	// !=运算符重载
	bool operator != (const Date& d);

	// 日期-日期 返回天数
	int operator-(const Date& d);

private:
	int _year;
	int _month;
	int _day;
};
bool Date::CheckLeap(int year)
{
	if ((0 == year % 4 && 0 != year % 100) || 0 == year % 400)
		return true;
	return false;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	if (1 == month || 3 == month || 5 == month || 7 == month || 8 == month || 10 == month || 12 == month)
		return 31;
	else if (2 == month)
	{
		if (CheckLeap(year))
			return 29;
		else
			return 28;
	}
	else
		return 30;
}

// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

// 拷贝构造函数
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}


// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}

// 析构函数
Date::~Date()
{
	
}

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

// 日期+天数
Date Date::operator+(int day)
{
	Date temp(*this);
	temp._day += day;
	while (temp._day > GetMonthDay(temp._year, temp._month))
	{
		temp._day -= GetMonthDay(temp._year, temp._month++);
		if (temp._month > 12)
		{
			temp._year += temp._month / 12;
			temp._month %= 12;
		}
	}
	return temp;
}

// 日期-天数
Date Date::operator-(int day)
{
	Date temp(*this);
	temp._day -= day;
	while (temp._day <= 0)
	{
		if (1 == temp._month)
		{
			temp._year = temp._year - 1;
			temp._month = temp._month + 12;
		}
		temp._month = temp._month - 1;
		temp._day = temp._day + GetMonthDay(temp._year, temp._month + 1);
	}
	return temp;
}

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

// 前置++
Date& Date::operator++()
{
	int carry = 0;
	_day += 1;
	carry = _day / GetMonthDay(_year, _month);
	if (carry > 0)
	{
		_day %= GetMonthDay(_year, _month);
		_month += carry;
		carry = _month / 12;
		if (carry > 0)
		{
			_month %= 12;
			_year += carry;
		}
	}
	return *this;
}

// 后置++
Date Date::operator++(int)
{
	Date temp(*this);
	int carry = 0;
	_day += 1;
	carry = _day / GetMonthDay(_year, _month);
	if (carry > 0)
	{
		_day %= GetMonthDay(_year, _month);
		_month += carry;
		carry = _month / 12;
		if (carry > 0)
		{
			_month %= 12;
			_year += carry;
		}
	}
	return temp;
}

// 后置--
Date Date::operator--(int)
{
	Date temp(*this);
	if (1 == _day)
	{
		if (1 == _month)
		{
			_year = _year - 1;
			_month = _month + 12;
		}
		_month = _month - 1;
		_day = _day + GetMonthDay(_year, _month + 1);
	}
	_day = _day - 1;
	return temp;
}

// 前置--
Date& Date::operator--()
{
	if (1 == _day)
	{
		if (1 == _month)
		{
			_year = _year - 1;
			_month = _month + 12;
		}
		_month = _month - 1;
		_day = _day + GetMonthDay(_year, _month + 1);
	}
	_day = _day - 1;
	return *this;
}

// >运算符重载
bool Date::operator>(const Date& d)
{
	if (_year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day <= d._day))
		return false;
	return true;
}

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

// >=运算符重载
inline bool Date::operator >= (const Date& d)
{
	if (_year < d._year || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day))
		return false;
	return true;
}

// <运算符重载
bool Date::operator < (const Date& d)
{
	if (_year > d._year || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day >= d._day))
		return false;
	return true;
}

// <=运算符重载
bool Date::operator <= (const Date& d)
{
	if (_year > d._year || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day))
		return false;
	return true;
}

// !=运算符重载
bool Date::operator != (const Date& d)
{
	if (_year != d._year || _month != d._month || _day != d._day)
		return true;
	return false;
}

// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
	int s1 = 0;
	int s2 = 0;
	for (int i = 0; i < _year; i++)
	{
		if (CheckLeap(i))
			s1++;
	}
	for (int i = 0; i < _month; i++)
	{
		s1 += GetMonthDay(_year, i);
	}
	s1 += _day + _year * 365;
	for (int i = 0; i < d._year; i++)
	{
		if (CheckLeap(i))
			s2++;
	}
	for (int i = 0; i < d._month; i++)
	{
		s2 += GetMonthDay(d._year, i);
	}
	s2 += d._day + d._year * 365;
	return (s1 - s2 > 0) ? s1 - s2 + 1 : s2 - s1 + 1;
}

以上就是这篇文章的所有内容啦,感谢老铁有耐心看完。有啥错误请多多指正哈!码字不易,希望大佬们点个赞
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RONIN_WZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值