C++入门篇——类与对象之类的六个默认成员函数
文章目录
构造函数的概念
如果一个类中什么成员都没有,简称为空类
空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
一、构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
所以构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象
其特征如下:
- 函数名与类名相同
- 无返回值
- 对象实例化时编译器自动调用对应的构造函数
- 构造函数可以重载
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
- C++把类型分成内置类型(基本类型)和自定义类型,内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型
内置类型的成员不会处理(C++11,声明支持给缺省值)
自定义类型的成员才会处理,回去调用这个成员的默认构造函数- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数- 总结:一般情况都需要我们自己写构造函数决定初始化方式,若成员变量全是自定义类型,可以考虑不写构造函数
我们以简单的Date类的构造函数为例,假设日期合法,代码如下
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
相关补充内容
初始化列表的使用
1.初始化列表是以一个冒号开始,接着以逗号分隔的数据成员列表,每个成员的后面使用的是括号括起来的初始化式.
2.以下情况必须使用带初始化列表的构造函数:
(1)类成员为const类型
(2)类成员为引用类型
(3)类的成员是对象成员初始化,而该对象没有无参构造函数
(4)如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数
3.在对初始化列表使用时,初始化的顺序是按照成员变量在类中声明次序,与其在初始化列表中的先后顺序无关
4.可以设置缺省值
explicit关键字
使用explicit关键字,将会禁止带参构造函数的隐式转换
静态成员变量
1.静态成员变量为所有类对象共享,不属于某一个实例
2.成员变量必须在类外定义,定义时不添加static关键字
3.类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
6.静态成员函数不可以调用非静态成员函数
7.非静态成员函数可以调用类的静态成员函数
二、析构函数
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
析构函数是特殊的成员函数,其特征如下:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数
注意:析构函数不能重载- 对象生命周期结束时,C++编译系统系统自动调用析构函数
- 析构函数与构造函数相似:内置类型的成员不会处理 自定义类型的成员会调用这个成员的析构函数
三、拷贝构造函数
在C语言中,传参或者拷贝一个结构体类型是非常复杂的,特别是涉及到除了栈区以外的内存申请空间,此时的传值拷贝并不能完全满足拷贝的要求,所以在C++中出现了拷贝构造函数来专门完成拷贝这件事情
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类–类型对象创建新对象时由编译器自动调用
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用
- 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
- 如果我们不写编译默认生成的拷贝构造函数,跟之前的构造函数特性不一样
1、内置类型,会发生值拷贝
2、自定义的类型,调用他的拷贝构造函数
所以在单纯的数值问题上,例如日期,天气,我们可以不写,用默认生成的
但一旦涉及到自主开辟内存空间的问题时就需要我们自己实现深拷贝的拷贝构造函数,默认生成会出问题
值得注意的是,我们在编写拷贝构造函数的时候函数的参数切记是引用而不是传值,后者传值会陷入无限死循环中
四、赋值运算符重载函数
运算符重载函数
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
运算符重载函数的特性
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载
- 参数类型,参数类型因为 const 类类型名 & 加对象名
- 返回值 返回的应该为 类类型名+& ,这样可以支持连续赋值
- 检查是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。这里只能是浅拷贝,即当前类中只有成员变量,没有资源
- 如果不显示定义,成员属性包含了自定义类型,则编译器会自动调用自定义函数的默认成员函数
Date& operator=(const Date& dt)//其中隐含了Date *this指针参数,用于接收符号左边的参数
{
if(this != &d)/判断是否是同一个对象,若不是同一个对象则进行赋值
{
_year=dt._year;
_month=dt._month;
_day=dt._day;
}
return *this; //返回对象本身作为引用接收
}
五、取地址操作符重载及const修饰的取地址操作符重载
这两个默认成员函数一般并不再进行重定义,编译器默认生成,只有特殊的情况,比如想让别人知道指定的内容的时候才需要进行重载
注意
1. const只修饰成员函数
2. operator&() 其中this的格式为:Date* const this 表示这个指针的指向不能更改
3. operator&()const , 其中this的格式为:const Date* const this ,此时既不能修改指针的指向也不能修改指针指向的内容
4. 读写的权限不能被放大,但是可以缩小
5. const对象可以调用非const成员函数吗?答:不可以,因为const对象表示其内容不能改变,而非const成员函数表示对象内容可以修改则会产生冲突。
6. 非const对象可以调用const成员函数吗? 答:可以,大的权限可以兼容小的权限
7. const成员函数内可以调用其它的非const成员函数吗?答,不可以,const成员函数中的this是不可修改的,而传递到非const中this可以读亦可以写,权限放大不允许
8. 非const成员函数内可以调用其它的const成员函数吗?答:可以,权限缩小