类和对象(类的默认6个成员函数以及操作符重载,日期类的实现)
目录
前言:
一.类的6个默认成员函数
1.构造函数
2.析构函数
3.拷贝构造
4.运算符重载
5,6.取地址及const取地址操作符重载
二.const成员
三.日期类
四.日期类完整代码
结言:
前言:
上一篇文章我们介绍了什么是面向对象什么是类,以及类的一些基本内容,那么今天我们就要学习关于类和对象的其他内容,废话不多说我们直接开启我们的新的内容吧!
一.类的6个默认成员函数
在上一篇文章中我们提到了一个概念空类,那么我们思考一下空类里面真的是什么都没有吗?
这肯定是不对的,在任何类中都会存在本身就定义好的函数默认成员函数,下面我来为大家一一介绍一些这6个函数。
1.构造函数
大家听到这个名称会不会联想到这难道是一个创建什么东西的函数吗?哦!不对如果你这么想就被他的名字给骗了。实际上他跟我们经常定义的Inint函数一些,没错它是一个初始化变量的函数。当我们没有对类的成员进行初始化时编译器会根据类中的构造函数进行初始化。不过这个函数也是有一些小的缺陷的。他对于内置类型不会进行初始化,只对于有构造函数的自定义类型的变量进行初始化。感觉有点绕呀,不过没关系下面我用一段代码来解释一下。
class A {
public:A(int n = 1){_a = n;_b = n;}
private:int _a;int _b;
};
class B {private:int _a;int _b;A _A1;
};int main()
{B B1(1);return 0;
}
这我们就可以清晰的理解上段话了吧。还有一点需要注意的是我们自己实现的构造函数名称计算类名称就像我们上诉代码中类A一样。当我们自己实现过构造函数后编译器就不会执行系统生成的构造函数了。
需要注意的是我们自己写的构造函数是可以缺省的,如果自己不想传参可以将构造函数设置为缺省函数。
同时缺省函数以及全缺省函数还有系统默认生成的函数都称为默认构造函数。
2.析构函数
析构函数和构造函数一般来说是成对的,构造函数对应的是Inint而析构函数对应的就是Destory。
概念:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
一般来说析构函数以~开头,值得注意的是析构函数是不支持重载的,只有一个析构函数,如果自己没有定义析构函数的话会运行系统自带的析构函数。
那么说完析构和构造函数后那么他们的调运顺序是怎样的呢?难道是定义完一个类之后就会运行析构吗还是直到程序运行完之后才进行析构呢?别慌我们下面用一串代码来解释一下。
class Stu {public:Stu(char n){_a = n;cout <<_a << "构造函数 " << endl;}~Stu(){cout << _a << "析构函数 " << endl;}
private:char _a;
};int main()
{Stu a('a');Stu b('b');return 0;
}
好我们根据这个代码思考一下,我们的程序运行后应该怎么去输出呢?
我们可以清晰的看到构造函数的调用是根据创建变量的顺序来的,但是析构函数却是相反的。这也就说明了他们分别的调用顺序是等到程序结束后析构函数才运行。
3.拷贝构造
class Stu {public:Stu(char n){_a = n;//cout <<_a << "构造函数 " << endl;}Stu(const Stu& n){_a = n._a;}void Print(){cout << _a << endl;}~Stu(){//cout << _a << "析构函数 " << endl;}
private:char _a;
};int main()
{Stu a('a');a.Print();Stu b('b');b.Print();Stu c(a);c.Print();return 0;
}
4.运算符重载
运算符重载是C++为了提升代码的简洁性而增添的概念,大家都知道内置类型之间可以直接进行加减乘除运算,但是我们自定义的运算却需要函数来解决,就可以大大的增加我们代码的简洁性了。
它的关键字为:operator。
那么它怎么用呢?
class date {
public:date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}bool operator < (date& n){if (_year < n._year){return true;}else if (_year == n._year){if (_month < n._month){return true;}else if (_month == n._month){if (_day < n._day)return true;}}return false;}private:int _year;int _month;int _day;
};int main()
{date a1(2004, 01, 21);date a2(2025, 01, 21);a1.Print();a2.Print();cout << (a1 < a2) << endl;return 0;
}
如上诉一个简单的日期代码我,如果没有运算符重载我们则需要写出函数的名称再才可以,但是有了函数符重载之后我们就可以提高我们代码的可读性。
这时候就有兄弟们要问了我们不是在讲默认成员函数吗?怎么讲到这个了。
没错我们的第4个默认成员函数就是赋值运算符重载,有了运算符重载的知识我们再来了解赋值运算符重载就很方便了。那么这具体是怎么一个东西呢?
class date {
public:date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}//bool operator < (date& n)//{// if (_year < n._year)// {// return true;// }// else if (_year == n._year)// {// if (_month < n._month)// {// return true;// }// else if (_month == n._month)// {// if (_day < n._day)// return true;// }// }// return false;//}private:int _year;int _month;int _day;
};int main()
{date a1(2004, 01, 21);date a2(2025, 01, 21);//a1.Print();//a2.Print();a1 = a2;a1.Print();return 0;
}
如上述代码我们并没有对=进行定义但是我们却可以直接使用,这就说明我们用到了系统给我们的赋值运算符重载
但是需要注意的是赋值运算符重载只能支持类里面的赋值,在全局变量的赋值是不支持的。
如果想要进行全局的赋值我们就需要自己写的赋值运算符重载写到外面。但是在外面我们就拿不到我们的this指针了,所以需要传两个参数。
然后有小伙伴就要问了,既然系统给了我们这么多的默认成员函数,拿我们是不是就不需要写这些东西了。嗯——,目前来说日期类是这样的但是对于一些比较复杂的类像栈这些就需要我们去自己些我们的这些函数了。总的来说就是当类中含有我们的自定义变量的时候就需要我们自己去实现这些函数了。
5,6.取地址及const取地址操作符重载
这两个一般来说不需要我们自己去定义,所以就把他们放到一起来讲。
同其他的运算符重载一样,直接对我们的自定义类型进行操作,但是系统给我们的默认函数完全足够我们使用。形式为:
date* operator()
date* operator()const
二.const成员
这里我们补充一个知识,可能有同学们看到date* operator()const这个代码会有点困惑,这个const是干什么的?为什么这里会有一个const。这里就会涉及到我们的另一个知识。
在上一篇文章我们知道了在类里面的函数一般都是会有一个this指针的但是它被隐藏了。那么现在就有一个问题它既然隐藏了那我们不想让别人修饰这个类变量的时候应该怎么办呢?总得给const安排一个地方把。所以C++就把这个const放到了函数的后面。所以当大家看到上面的代码时,不要困惑这是正常的。
三.日期类
我相信对于大家来说完整的写一个日期类是简单的,但是里面依旧有一些需要注意的知识点。正好我们通过一个完整的日期类来解决。
一个完整的类肯定是要包括比较两个日期的大小以及两个日期相差多少天这种情况。
在上面我们实现了日期小于的比较,那么我们大于以及等于的比较就很轻松了。这里有个问题既然我们已经有了小于的比较大于和等于的逻辑我们还用再写一边吗?
答案是否定的。
bool operator ==(date& n){if (_year == n._year && _month == n._month && _day == n._day)return true;else{return false;}}bool operator >(date& n){if (_year == n._year && _month == n._month && _day == n._day){return false;}return !(*this < n);}
接下来是大于等于,小于等于以及不等于,同样我们直接使用我们以及实现过的重载就可以实现
bool operator >=(date& n){return !(*this < n);}bool operator <=(date& n){return !(*this > n);}bool operator !=(date& n){return !(*this == n);}
这样我们就将一些简单的比较实现了,接下来我们就需要实现两个日期相差多少天的实现了。这里面主要的难点就是现实中月份是不一样的,同时还有闰年的影响。为了更方便的实现我们的功能我们把获取每月的天数分开来写。
int GetMon(int year, int month){assert(month > 0 && month < 13);int monthday[13] = { -1,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 monthday[month];}}
好这样我们就可以去实现日期的加等和减等了
date operator+=(int day){_day += day;while(_day > GetMon(_year,_month)){_month++;if (_month > 12){_year++;_month = 1;}_day -= GetMon(_year, _month);}return *this;}date operator-=(int day){_day -= day;while (_day <= 0){_month--;if (_month <= 0){_year--;_month = 12;}_day += GetMon(_year, _month);}return *this;}
既然加等和减等有了的话我们的加减也就很容易实现了无非就是不改变原来的值就可以了,我们只需要把原本的日期拷贝给另一个类就可以了。
date operator-(int day){date ret = *this;ret -= day;return ret;}date operator+(int day){date ret = *this;ret += day;return ret;}
这时我们需要提到我们极为特殊的运算符++以及--,在C语言中我们知道++和--分为前置和后置同时这两种的结果也是不一样的。不过我们可以换一个思路前置++不就相当于我们的+=1吗而后置就相当于+1.这样我们就可以用我们已经实现了的+=和+来实现。
同时C++为了区分前置和后置而向后置++中加了一个int。
date operator++(){return *this += 1;}date operator++(int){date tem = *this;*this += 1;return tem;}
--与++类似就不再写了,最后我们最重要的就是计算两个日期之间间隔了多少天。这个就需要涉及到我们的++了,当然用常规的方法也是可以的但是需要考虑的因素太多容易出错。下面我就为大家介绍一下如何实现
int operator - (date & d){date max = *this;date min = d;int flag = 1;if (max < min){max = d;min = *this;flag = -1;}int count = 0;while (max > min){++min;count++;}return flag*count;}
好了一个基本完整的日期类就完成了。下面我给大家放出完整的代码。
四.日期类完整代码
class date {
public:date(int year=0, int month=0, int day=0){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}bool operator < (date& n){if (_year < n._year){return true;}else if (_year == n._year){if (_month < n._month){return true;}else if (_month == n._month){if (_day < n._day)return true;}}return false;}bool operator ==(date& n){if (_year == n._year && _month == n._month && _day == n._day)return true;else{return false;}}bool operator >(date& n){if (_year == n._year && _month == n._month && _day == n._day){return false;}return !(*this < n);}bool operator >=(date& n){return !(*this < n);}bool operator <=(date& n){return !(*this > n);}bool operator !=(date& n){return !(*this == n);}int GetMon(int year, int month){assert(month > 0 && month < 13);int monthday[13] = { -1,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 monthday[month];}}date operator+=(int day){_day += day;while(_day > GetMon(_year,_month)){_month++;if (_month > 12){_year++;_month = 1;}_day -= GetMon(_year, _month);}return *this;}date operator-=(int day){_day -= day;while (_day <= 0){_month--;if (_month <= 0){_year--;_month = 12;}_day += GetMon(_year, _month);}return *this;}date operator-(int day){date ret = *this;ret -= day;return ret;}date operator+(int day){date ret = *this;ret += day;return ret;}date operator++(){return *this += 1;}date operator++(int){date tem = *this;*this += 1;return tem;}int operator - (date & d){date max = *this;date min = d;int flag = 1;if (max < min){max = d;min = *this;flag = -1;}int count = 0;while (max > min){++min;count++;}return flag*count;}
private:int _year;int _month;int _day;
};int main()
{date a1(2025, 01, 21);date a2(2025, 02, 01);//a1.Print();//a2.Print();//a1 = a2;//a1.Print();//cout << (a1 > a2) << endl;//cout << (a1 == a2) << endl;//cout << (a1 < a2) << endl;//cout << (a1 >= a2) << endl;//cout << (a1 <= a2) << endl;cout << (a1 - a2) << endl;//a1 += 11;//a1.Print();//a1 -= 21;//a1.Print();//(a1 - 120).Print();return 0;
}
结言:
今天我们详细的介绍了一下类中的6个默认函数,运算符重载以及一个完整日期类的实现。当然这也只是C++知识的冰山一角。喜欢博主文章的可以关注一下,博主会持续更新一些关于C++知识的文章。我们大家一起努力加油!