在空类中,不是什么都没有的,会默认生成六个成员函数;
本篇文章记录的是前四个默认成员:
目录
1. 构造函数
概念
特征
几类构造函数
a. 自动生成的默认构造函数
b. 无参构造函数(默认构造函数)
c. 带参的构造函数
d. 全缺省构造函数(也是默认构造函数的一种)
重载
总结
2. 析构函数
概念
特征
析构顺序
3. 构造拷贝函数
概念
特征
传值传参递归问题
4.运算符重载
概念
类外
类中
调用方式更改:
优化
以日期类为例:
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. 构造函数可以重载。
顾名思义,函数的创建,也就是函数的初始化
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
特征
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
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;
}
当对象没有显示构造函数时:
生成默认构造函数
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.编译器生成的默认析构函数,会对自定类型成员调用它的析构函数。
严格来说,日期类是不需要写析构函数的,内置类型随着程序结束时,内置类型成员也随之销毁。但是手动开辟的空间需要些析构函数进行数据处理,要不然会造成内存泄露的问题这就是对内置类型不处理的劣势。因此对于手动开辟空间的内置类型要写出析构函数。
如栈:
析构顺序
相同类创建的对象,析构顺序与构建顺序相反
如果一个类中的成员全部都是自定义类型,那么可以使用默认生成的函数
如果有内置类型成员或者需要显示传参初始化,那么都需要自己实现构造函数
不传参就可以调用的就是默认构造函数
概念
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特征
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
~Date()
{
}
~Date()
{
_year = 1;
_month = 1;
_day = 1;
}
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
也遵循对内置类型不处理,对自定义类型调用他自己的析构函数处理的原则。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
5.编译器生成的默认析构函数,会对自定类型成员调用它的析构函数。
严格来说,日期类是不需要写析构函数的,内置类型随着程序结束时,内置类型成员也随之销毁。但是手动开辟的空间需要些析构函数进行数据处理,要不然会造成内存泄露的问题这就是对内置类型不处理的劣势。因此对于手动开辟空间的内置类型要写出析构函数。
如栈:
析构顺序
相同类创建的对象,析构顺序与构建顺序相反
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
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.运算符重载
概念
运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
只有单个形参,该形参是对本类类型对象的引用(一般常用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进行清理:
这样类型的对象进行构造拷贝,不能使用浅拷贝,要使用深拷贝才行,在之后的笔记中会详细讲到。
运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
为了让自定义类型可以使用各种运算符,可以进行运算符重载
比如想要比较两个日期是否相同:
不可以直接进行比较,那么我们可以使用运算符重载
函数名字为:关键字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
这里对运算符重载做了一个简单的介绍,简单应用,下一篇文章将以日期类对运算符重载进行详解,这里有什么不对的地方欢迎各位评论指正,一起进步!



