c++类和对象(中)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

接着学习c++的类和对象


提示:以下是本篇文章正文内容,下面案例可供参考

一、类的默认成员函数

如果对于一个类,我们不予任何函数和变量,那么它里面真的就什么都没有吗。
其实并不是,对于空类,编译器会自动生成下面几个默认成员变量
在这里插入图片描述
其中,前四个是比较重要的,需要重点讲解的

二、构造函数

1.概念

在这里插入图片描述
对于这样一个类,在创建一个对象之后,还需要用init函数为其初识化,那么有没有一种方法在对象创建的同时为其初始化呢?这就是构造函数的作用。

构造函数是一种特殊的成员函数,函数名和类名相同,在编译时自动调用,使每一个对象都有一个合适的初始值。

#include<iostream>
using namespace std;

class Date
{
public:
	//void Init(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	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(2003, 3, 13);
	d1.Print();


	return 0;
}

2.特性

构造函数的作用就是初识化对象,对于使用也有一定规范
1、函数名要和类名相同
2、无返回值
3、对象实例化(上章有提到)时,编译器自动调用
4、支持函数重载(注意二义性)
5、在使用无参的构造函数时,不能在对象后面加上括号,否则可能变成函数声明

Date d(void);

6、如果用户没有给显式的构造函数定义,那么编译器会自动生成一个无参的构造函数
有很多人肯定想问,编译器给的构造函数能用吗,未知的东西没有什么使用价值。
7、c++中的成员变量类型分为内置类型和自定义类型,对于内置类型,我们可以在定义时手动给上缺省值。而对于自定义类型,编译器会自动调用它的默认成员函数。

#include<iostream>
using namespace std;

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	//void Init(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	//Date(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 0;
	int _month = 0;
	int _day = 8;
	Time _t;
};

int main()
{
	Date d1;
	d1.Print();


	return 0;
}

在这里插入图片描述

8、无参构造函数,全缺省构造函数,没写时编译器自动生成的构造函数都叫默认构造函数。在使用无参来创建一个变量时(Date d),默认构造函数必须存在且只能存在一个。

三、析构函数

1、概念

一个对象通过构造函数可以很方便的初始化,那么在对象使用完成后,这个对象是如何销毁的呢,其实并不是通过析构函数来进行销毁的。

对象在销毁时会自动调用析构函数来对其内的资源进行集中销毁。

2、特性

1、析构函数函数名是在类名前加上~
2、无参数和返回值
3、如果用户没有写,编译器会自动生成
4、在对象的生命周期结束时,编译器自动调用
5、在自定义类型存在时,会自动调用自定义类型的析构函数。
6、如果类中没有申请空间,只有一些内置类型变量,析构函数可以不写,使用编译器自带的析构函数就行

四、拷贝构造函数

1、概念

顾名思义,拷贝构造函数是构造函数的另一种函数重载,在创建对象时,创造一个和所给对象完全相同的对象,由编译器自己调用。

2、特性

参数只有一个,是所给的对象的引用传参,如果使用传值调用会导致无穷递归而报错。

#include<iostream>
using namespace std;

class Date
{
public:

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 0;
	int _month = 0;
	int _day = 8;
};



int main()
{
	Date d(2022, 10, 3);
	Date d2(d);

	d.Print();
	d2.Print();
	return 0;
}

在这里插入图片描述
若未显式定义,编译器会使用默认拷贝构造函数。但只是浅拷贝,需要和后面学的深拷贝相结合,印证。
注:类中如果没有涉及到内存申请,是否写拷贝构造函数都行,一旦遇到内存申请,就必须要自己写,否则就是浅拷贝。

五、赋值运算符重载

1、运算符重载

在c++中,为了增强代码的可读性,加入了运算符重载这个概念
函数原型:返回值类型 operator 操作符(参数)

需要注意
在这里插入图片描述

#include<iostream>
using namespace std;

class Date
{
public:

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator ==(const Date& d)
	{
		return _year == d._year 
			&& _month == d._month
			&& _day == d._day;

	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 0;
	int _month = 0;
	int _day = 8;
};



int main()
{
	Date d(2022, 10, 3);
	Date d2(d);
	cout << (d == d2) << endl;
	d.Print();
	d2.Print();
	return 0;
}

在这里插入图片描述

这里的d==d2会在编译时自动转化为d.opeartor == (d2)所以上面在写函数时指望里面传一个参数,剩下的一个由this指针代替。

2、赋值运算符

	//赋值运算符
	void operator =(const Date& d)
	{
		_year = d._year;
		_day = d._day;
		_month = d._month;
	}

像这样的写法看似没什么问题,但还是不够完美,在给内置类型赋值时,我们常常会使用连续赋值的方法给多个数进行赋值,我们所写的方法就没有这个功能。
这里稍微做出一点改善

	Date& operator =(const Date& d)
	{
		_year = d._year;
		_day = d._day;
		_month = d._month;
		return *this;
	}

这里使用引用传参,虽然this指针会在出了函数作用域后销毁,但*this是d,不会销毁,引用传参的效率更高。

六、日期类的实现

具体代码如下
Date.h

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

class Date
{
public:
	int GetDay(int year, int month)//得到所给年,月所对于的天数
	{
		int arr[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;
		}//为闰年时
		else
			return arr[month];
	}

	//构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		if (!((year >= 1)
			&& (_month >= 1 && _month <= 12) &&
			(_day >= 1 && _day <= GetDay(_year, _month))))
		{
			cout<<"日期输入错误"<<endl;
			exit(-1);
		}
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	Date& 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);

	bool operator != (const Date& d);

	int operator - (const Date& d);//日期与日期相减

	Date& operator += (int day);

	Date operator + (int day);

	Date& operator -= (int day);

	Date operator - (int day);

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

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

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

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


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


Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include"Date.h"

Date& Date::operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}


bool Date::operator == (const Date& d)
{
	return (_year == d._year 
		&& _month == d._month 
		&&_day == d._day);
}


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 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)
{
	if (day < 0)
		return *this -= abs(day);
	_day += day;
	while (_day > GetDay(_year, _month))
	{
		_day -= GetDay(_year, _month);
		_month++;
		if (_month > 12)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;

}


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

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

	_day -= day;
	while (_day < 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetDay(_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)
	{
		flag = -1;
		max = d;
		min = *this;
	}
	int count = 0;
	while (min != *this)
	{
		min++;
		count++;
	}
	return count;
}

Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include"Date.h"

void Test1()
{
	Date d1(2022, 10, 17);
	Date d2 = d1;
	Date d3(2022, 10, 17);
	d1.Print();
	d3.Print();
	cout << (d3<=d1) << endl;
}


void Test2()
{
	Date d1(2022, 10, 17);
	Date d3(2022, 10, 27);
	d1.Print();
	d3.Print();
	cout << (d3 - d1) << endl;
	(++d3).Print();

}

int main()
{
	//Test1();
	Test2();
	return 0;
}

七、输入流和输出流

我们都知道,c++在输出和输入时,使用cin和cout,但这只能对内置类型进行识别并输出,首先来研究一下它时如何识别的,实际上,cin和cout只是库里面的两个函数,进行重载后可以对内置类型进行识别

在这里插入图片描述

如果我们想要用cin和cout来对我们上面创建的类(日期)进行输入和打印,自然也缺少不了函数重载
从iostream可以清晰的看出,流提取和流插入,分别是属于ostream和istream。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

从图片中可以看出,若想打印,必须使用d<<cout(d.operator(cout))这样的打印方式,很显然是不方便的。那么如何克服这种this指针强制占据第一位的问题呢。

其实很简单,我们只需要将这个重载函数从类中拿出来,自己给它设置两个参数。但另一个问题又来了:我们将函数拿出来后,会面临私有成员无法访问的尴尬问题。这边有两个解决方案,一个是冒险将成员变量公开化,另一种就是使用友元的用法,这边我们下一期详谈。

我们成功使用cout后,在内置类型使用时,往往会有cout<<a<<b<<c<<endl这种情况,这就要我们在函数重载的时候把返回值给设为ostream&(cout在流时是从左边开始一个一个流取的)。

总结

以上就是类和对象里比较重要的一部分内容。通过一个日期类和对象的代码,展开了一系列问题,每一个问题都值得深思,后面也会有很多的其他内容,欢迎继续关注,如有问题,还请指出,万分感谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值