目录
1.前言
上篇博客记录了,类的基本属性以及this指针。这篇博客将继续讨论类和对象的特性。本篇博客会写到类的6个默认成员函数,着重讨论构造函数、析构函数、拷贝构造以及运算符重载。
2.六大默认成员函数
这六个函数的作用在上图已经描述。解释一下这个复制和拷贝的区别:拷贝构造的时候,对象还没有被创建,而赋值的时候对象已经创建。
3.构造函数和析构函数
3.1构造函数
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
如上代码,这里我写了一个日期类,里面包含年月日。这里的Date函数就是构造函数,这个函数比较特殊,这个函数没有返回值,而且名字与类的名字相同。这也是构造函数的特性,有且满足上诉两个条件的类内部的函数才是构造函数。
3.2析构函数
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
_weather = (char*)malloc(sizeof(char)*10);
}
~Date()
{
free (_weather);
}
private:
int _year;
int _month;
int _day;
char* _weather;
};
如上代码,这个~Date()函数就是析构函数,析构函数会在对象生命周期结束时自动调用。对于自定义类型如果不去处理,内置类型开辟的空间就会随着对象的销毁返还给操作系统,所以不会产生很大的影响,但是如果是动态申请的内存,对象在销毁时就会销毁这个指针,导致动态开辟的内存无法找到进行销毁,从而造成内存泄漏。所以如果对象中的成员包含非内置类型,那么析构函数是很有必要要写的,不然可能会造成一定的问题。
4.拷贝构造
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
_weather = (char*)malloc(sizeof(char)*10);
}
~Date()
{
free (_weather);
}
private:
int _year;
int _month;
int _day;
char* _weather;
};
还是如上代码,看似没有问题,其实这里存在一个很大的隐患:这里没有写拷贝构造,那么就会生成一个默认的拷贝构造,而默认的拷贝构造实现的是浅拷贝,也就是把一个对象里面的所有值都拷贝过去,不经过任何处理。那么我们在拷贝_weather的时候就会直接把指针拷贝过去,那么在free的时候就会对同一块地址进行free,也就是释放了两次内存导致程序崩溃。
现在让我们想想,我们在拷贝这个指针的时候是希望拷贝里面的内容还是说单纯的拷贝这个指针。那在我们这个类中,当然是希望拷贝里面的内容,所以我们就需要特殊处理一下。
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
_weather = (char*)malloc(sizeof(char)*10);
}
Date(const Date& a)
{
_year = a._year;
_month = a._month;
_day = a._day;
_weather = (char*)malloc(sizeof(char) * 10);
for (int i = 0; i < 10; i++)
_weather[i] = a._weather[i];
}
~Date()
{
free (_weather);
}
private:
int _year;
int _month;
int _day;
char* _weather;
};
这里的Date(const Date& a)就是构造函数,这个函数与构造函数类似,也是没有返回值函数名与类名一样,不一样的是,这个函数的参数是一个类的引用。这里为什么要用引用呢?这里就要回归到C语言的知识了,如果这里不是引用,那么这里的a将会是一个原对象的临时拷贝,既然是临时拷贝,那么就需要调用拷贝构造,而我们在进行拷贝构造前又要进行拷贝,所以就陷入了死循环,永远无法进入拷贝构造,因为如果没有引用这个函数就会在 const Date a 这里一直调用拷贝构造。
5.运算符重载
运算符重载,顾名思义就是给运算符不同含义。比如我们日常生活中的两个日期相减,算出这俩日期相差的日子,日期加上一个数字,计算出10天后是星期几。这些就是这个类的加减法的运算符重载。运算符重载可以极大方便类的使用者进行编程。
bool Date::operator==(const Date& d)const
{
return (_year == d._year && _month == d._month && _day == d._day);
}
bool Date::operator>(const Date & d)const
{
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;
return false;
}
bool Date::operator>=(const Date& d)const
{
return (*this == d || *this > d);
}
bool Date::operator<(const Date& d)const
{
return !(*this >= d);
}
bool Date::operator<=(const Date& d)const
{
return !(*this > d);
}
bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
int Date::get_month_day(int year, int month)
{
static int day[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 day[month];
}
Date Date::operator+(const int d)const
{
Date tmp = *this;
tmp += d;
return tmp;
}
Date& Date::operator-=(const int d)
{
if (d < 0)
{
*this += -d;
return *this;
}
_day -= d;
while (_day < 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += get_month_day(_year,_month);
}
return *this;
}
Date Date::operator-(const int d)const
{
Date tmp = *this;
tmp -= d;
return tmp;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp = *this;
++(*this);
return tmp;
}
int Date::operator-(const Date& d)const
{
int sum = 0;
int flag = 1;
if (*this < d)
flag = -1;
Date Min = (*this) < d ? (*this) : d;
Date Max = (*this) > d ? (*this) : d;
while (Min != Max)
{
++sum;
++Min;
}
return sum*flag;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
--(*this);
return tmp;
}
这里我就不细说了,这里就是我对日期类一些运算符的重载。
6.结语
今天复习了,构造函数、析构函数和拷贝构造。后续会更新类和对象的一些特性,如果有问题欢迎一起讨论一起进步呀