目录
1.类的6个默认成员函数
2.构造函数
特征
class Date
{
public:
// 1.无参构造函数
Date()
{}
// 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
比特就业课
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1; // 调用无参构造函数
Date d2(2015, 1, 1); // 调用带参的构造函数
Date d3();声明了d3函数,该函数无参,返回一个日期类型的对象
5.
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
6.
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类

在C++中,默认构造函数对内置类型(如int,char等)类型不做处理,对自定义类型调用自定义类型的构造函数。这也是我们看到随机值的原因。
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
3.析构函数
特性
class PEO
{
public:
~PEO()
{
cout << "析构函数:~PEO()" << endl;
}
int _age;//年龄
int _height;//身高
int* _tmp;
};
class STU
{
public:
private:
//成员变量
int _math;//学生数学成绩
int _chinese;//学生语文成绩
int _english;//学生英语成绩
PEO peo;
};
int main()
{
STU stu1;
return 0;
}
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。
总结一句话就是,析构函数和构造函数一样,对内置类型不做处理,对自定义类型调用相应的析构函数。
构造和析构一些题
1.构造顺序是按照语句的顺序进行构造,析构是按照构造的相反顺序进行析构
对象析构要在生存作用域结束的时候才进行析构
在函数F中,本地变量a和b的构造函数(constructor)和析构函数(destructor)的调用顺序是: ( )
Class A;
Class B;
void F() {
A a;
B b;
}
先构造,a,b构造,然后析构,b,a析构
2.
注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象
全局对象先于局部对象进行构造
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )
C c;
int main()
{
A a;
B b;
static D d;
return 0;
}
构造的顺序为c a b d
析构的顺序为 b a d c (static修饰的变量会放在局部对象之后进行析构)
4.拷贝构造函数
构造函数是创建对象时的初始化,而拷贝构造函数可以理解为拷贝一个对象初始化的值。
特征
class Date
{
public:
Date(int year = 1, 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;
}
void show()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 1, 1);
Date d2(d1);
d1.show();
d2.show();
return 0;
}
创建d1对象后,想拷贝一份给d2,因为d1是自定义类型,调用拷贝构造函数Date,d是d1的别名,直接访问成员变量赋值给d2的成员变量。
思考一下,直接传值为什么不行?
Date(const Date d)//不传引用会出现什么情况
{
_year = d._year;
_month = d._month;
_day = d._day;
}
如果不加引用,直接传值的话,会出现无限递归拷贝的情况。
为什么传值就需要拷贝构造呢?以前写的很多函数都有传值传参啊。
这是因为内置类型(int、char)编译器很熟悉如何拷贝,需要拷贝多少字节。
但是这里是自定义类型,有很多难以直接拷贝的类型(如:链表、树),所以需要用到拷贝构造。
每一次实参传值给形参,都需要拷贝构造一份实参再继续下去,就形成了无限递归,因此编译器禁止这种行为。
用引用则不会产生这种情况,因为引用d相当于是d1的别名,直接操纵d1的成员变量。
此外,在引用前加const是为了防止前后位置写反(d._year = _year的情况)。这样编译器能检查出来。
需要写析构函数~Stack( )的,需要深拷贝;
不需要写析构函数~Stack( )的,编译器默认的浅拷贝即可。
5.赋值运算符重载
对于内置类型,运算符可以直接使用(如:int a = 1,b = 2; a+b;)
如果是自定义类型想要直接使用运算符,就需要用到运算符重载(与函数重载没有关系)

一个日期类要写相等应该怎么写呢?
#include<iostream>
using namespace std;
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;
};
int main()
{
Date d1(2023,10,17);
Date d2(2023,10,17);
return 0;
}
判断是否相等,返回值类型可以用bool,函数名是operator后的运算符,参数必须是类类型的。
(bool如果相等返回值是1,如果不相等返回值是0)
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator == (const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 10, 17);
Date d2(2023, 10, 17);
cout << (d1 == d2) << endl;
return 0;
}
显示函数的参数多了,我们传参传两个,用两个参数接收怎么会多呢?————因为有隐含的this指针。这里正因为有这个隐含的this指针作了一个参数,所以会显示参数过多。
因此,我们拿掉一个参数,就传一个,this指针相当于d1,d就是d2的别名
这里传参要引用传参,因为传值传参会多一个拷贝构造(自定义类型默认),并且比较两个对象不做修改,用const修饰更好。
大于的写法
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator>(const Date& d)
{
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;
else
return false;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 10, 3);
Date d2(2023, 10, 2);
cout << (d1 > d2) << endl;
return 0;
}
注意:这里有一种简便写法,以大于等于>=为例,既然上面已经分别写好了等于和大于的重载,那么大于等于可以直接复用:
bool operator>=(const Date& d)
{
return *this > d || *this == d;
}
比较的运算符重载完成后,我们来看一下 " +=" 是如何操作的。
首先写一个能得到月份天数的函数
class Date
{
public:
int GetMonthday(int year, int month)
{
static int monthday[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 monthday[month];
}
实现+=的日期
Date& operator+=(int day)
{
_day += day;
while (_day > GetMonthday(_year,_month))
{
_day -= GetMonthday(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month -= 12;
}
}
return *this;
}
注意这里是引用返回,因为我们写的是 += ,会改变日期,如果不用引用返回,*this出作用域之前会先拷贝,用引用返回就省了拷贝。
那不改变日期的显然就是 " + " 了,那么就要用到拷贝构造,不需要引用返回了。
实现+的日期
Date operator+(int day)
{
Date ret(*this);
ret += day;
return ret;
}
int main()
{
Date d2(2022,1,2);
d2 + 1000;
return 0;
}