在空类中,不是什么都没有的,会默认生成六个成员函数;
本篇文章记录的是前四个默认成员:
目录
以日期类为例:
class Date
{
public:
void DateInit(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;
};
1. 构造函数
概念
顾名思义,函数的创建,也就是函数的初始化
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
特征
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
日期类中,我们用的是DateInit函数对类进行初始化,其实,没有此函数的话,类会生成默认的构造函数,进行初始化。
几类构造函数
a. 自动生成的默认构造函数
当对象没有显示构造函数时:
生成默认构造函数
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
return 0;
}
b. 无参构造函数(默认构造函数)
//2.无参数构造函数
Date()
{}
//2.无参数构造函数
Date()
{
_year = 2022;
_month = 5;
_day = 15;
}
c. 带参的构造函数
//3.带参的构造函数
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
正确创建如下:
一般使用带参的构造函数时,将参数都写为缺省参数,更方便使用。
d. 全缺省构造函数(也是默认构造函数的一种)
//3.带参的构造函数
Date(int year=2000,int month=1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
重载
随机值原因:
编译器生成的默认构造函数对于内置类型的成员不做处理
对于自定义类型的成员才会做处理
创建自定义类型
总结
如果一个类中的成员全部都是自定义类型,那么可以使用默认生成的函数
如果有内置类型成员或者需要显示传参初始化,那么都需要自己实现构造函数
不传参就可以调用的就是默认构造函数
2. 析构函数
概念
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特征
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。~Date() { }
~Date() { _year = 1; _month = 1; _day = 1; }
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
也遵循对内置类型不处理,对自定义类型调用他自己的析构函数处理的原则。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
5.编译器生成的默认析构函数,会对自定类型成员调用它的析构函数。
严格来说,日期类是不需要写析构函数的,内置类型随着程序结束时,内置类型成员也随之销毁。但是手动开辟的空间需要些析构函数进行数据处理,要不然会造成内存泄露的问题这就是对内置类型不处理的劣势。因此对于手动开辟空间的内置类型要写出析构函数。
如栈:
析构顺序
相同类创建的对象,析构顺序与构建顺序相反
创建两个栈的对象
析构的顺序与构造的顺序相反,ST2后构造,那么他就先析构
3. 构造拷贝函数
概念
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
就是用一个已存在的对象取创建另一个一摸一样的对象,相当于复制粘贴
特征
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。如下:
传值传参递归问题
同类型的对象传值传参要调用拷贝构造
如果不加引用的话:无限递归
尽量前面加上const,因为参数不需要改变
//拷贝构造函数 Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; }
3. 若未显示定义,系统生成默认的拷贝构造函数。
默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
4.有动态开辟空间的对象不能进行如下拷贝构造
浅拷贝:
//拷贝构造->浅拷贝 Stack(const Stack& ST) { _a = ST._a; _top = ST._top; _capacity = ST._capacity; }
当程序结束时,析构函数开始进行数据清理:
先对ST2进行清理:
再对ST1进行清理:
这样类型的对象进行构造拷贝,不能使用浅拷贝,要使用深拷贝才行,在之后的笔记中会详细讲到。
4.运算符重载
概念
运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
为了让自定义类型可以使用各种运算符,可以进行运算符重载
比如想要比较两个日期是否相同:
不可以直接进行比较,那么我们可以使用运算符重载
函数名字为:关键字operator后面接需要重载的运算符符号。
类外
//参数:看运算符来定:两个相比较,那么就有两个形参
//返回值:看运算后的结构,比较相等返回真假即可,即bool
bool operator==(Date d1, Date d2)
{
return d1._year == d2._year&&
d1._month == d2._month&&
d1._day == d2._day;
}
这种方式不推荐
类中
原因是,这里存在三个形参,还有一个隐含的this指针,因此要去掉一个参数 ,且调用时也要改变,如下:
编译器会处理为:d1.operator==(&d1,d2)
调用方式更改:
优化
对上面传参的部分可以进行优化,传值调用会进行拷贝构造,这里应该传引用,并且对参数不更改,应加const
bool operator==(const Date& d2)
{
return _year == d2._year&&
_month == d2._month&&
_day == d2._day;
}
不能被重载的符号
域作用符 ::
sizeof
三目运算符?: . 操作符 .*操作符
注意事项
重载运算符必须有一个类类型参数,不能对内置类型进行重载
用于内置类型的运算符,不能改变其含义,比如重载+时,内容不能创建成 -
作为类成员的重载函数,形参比操作数目看起来少一个参数,因为有一个默认的形参this
这里对运算符重载做了一个简单的介绍,简单应用,下一篇文章将以日期类对运算符重载进行详解,这里有什么不对的地方欢迎各位评论指正,一起进步!