类和对象 中篇
在上篇中,我们介绍了类的基础部分,本篇我们讲解C++类的六大默认成员函数
所谓默认成员函数,就是我们不写编译器也会自动生成,自动调用的函数。而自动生成的函数对内置类型的成员不会处理(有些高版本编译器会处理,且C++11以后支持声明给缺省值),对自定义类型的成员会去调用对应的函数。
默认成员函数最大的用处就是自动调用,我们在C语言中写栈、顺序表的时候经常忘记初始化和置空,而默认成员函数很好的解决了这一点,只需要执行代码,之后的初始化和置空等操作都是自动进行,我们就不需要再去手动完成。
下面开始依次介绍默认成员函数🧐
构造函数🤔
构造函数是特殊的成员函数,它主要用于初始化对象,其特征为:
- 函数名与类名相同
- 无返回值
- 对象实例化时编译器自动调用对应的构造函数
- 可以对其重载
如下图,对象d1在没有调用Date函数的情况下,自动完成了初始化。
并且在C++11后的版本,支持内置类型给全省值
注意,以下写法是错误的,因为编译器不知道是声明还是创建对象
对于内置类型,编译器不会处理,而自定义类型编译器会去调用该类型的默认构造,如果没有默认构造就会报错。
最后,默认构造函数只能存在一个(不包括重载):全缺省构造函数,无参构造函数,没写自动生成的构造函数,如果写了构造函数,那么将不再自动生成。
析构函数🤔
析构函数与构造函数功能相反,用于对象的销毁,当对象生命周期结束时会自动调用析构函数,完成资源清理的工作,它的特征为:
- 析构函数要在类名前加上~
- 无参无返回值
- 一个类仅有一个析构函数,且不能重载
- 对象声生命周期结束时会自动调用析构函数
- 先定义的对象后清理(定义对象需要建立栈帧,栈的结构为先进后出)
如下图析构函数的自动调用实例,构造函数初始化a,析构函数将a置空
默认析构函数与构造函数相同,对内置类型不处理,自定义类型会调用该类型的析构函数。
注意:析构函数在下图情况会崩溃,原因是没有写拷贝构造,导致这里的赋值是直接将地址传给st2,造成浅拷贝。st1和st2共用一个空间,当两个对象生命周期结束时,st2会先进行析构,此时空间被销毁了,st1再去析构就访问到野指针,故程序崩溃。
拷贝构造🤔
拷贝构造函数也是特殊的成员函数,特征如下:
拷贝构造是构造函数的一个重载形式。
拷贝构造的参数只有一个,且只能是类类型对象的引用,如果不加引用会导致无穷递归(传参生成临时变量,临时变量接收参数,此时就是拷贝,拷贝又会去调用拷贝构造,造成套娃)
两种拷贝方式
参考栈的拷贝构造,我们建立一个新的空间,再将数据全部赋过去,st1和st2就成了两个独立空间,再进行析构就没有问题了。
拷贝构造对内置类型浅拷贝,自定义类型会调用该类型的拷贝构造。赋值是两个存在的对象进行拷贝,拷贝构造是一个已经存在的对象去初始化另一个要创建的对象。
运算符重载🤔
运算符无法识别非内置类型之间的比较,这时运算符重载就派上用场了
运算符重载需要用到关键字operator,其格式为(const可加可不加,后面会介绍):
返回值 operator运算符(参数) { //... } //如 bool operator<(Date& d) { //... }
运算符重载的特性为:
- 不能自己创建C++没有的操作符,比如operator@
- 用于内置类型的运算符,含义无法改变
- 重载操作符必须有一个类类型参数(在类中有this作为类类型参数)
- 作为类成员函数重载时,形参看起来比操作数少,是因为成员函数的第一个参数为隐藏的this
- .* sizeof . ?: :: 这五个运算符不能重载
取地址运算符的重载(const和非const)🤔
取地址(&)运算符其实也无法识别非内置类型,但是编译器将其函数加入了默认成员函数,所以我们可以不写也能使用该运算符。但是当我们不想让别人取到对象的地址时,就可以手动写对其修改。如下图我们传回nullptr,那么别人就拿不到对象的地址了。
Date* Date::operator&() { return nullptr; //传回空 } const Date* Date::operator&() const { return this; //传回地址 }
>
此外,如果对象被const修饰,那么就会调用const版取地址运算符的重载 注意:这里流插入运算符是重载后才能使用,代码如下:
ostream& operator<<(ostream& out, const Date& d) //双操作数,左操作数为第一个参数,成员函数this只能在第一位,所以不能在类里面定义 { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; } istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
成员函数const的用法🤔
在成员函数中,由于this指针是隐式的,且无法显式的写出来,所以当我们传给成员函数一个const修饰的对象就会出现这个问题:
void Print() { cout << _year << "年" << _month << "月" << _day << "日" << endl; }
Print接收的是Date* this的参数,但我们传过去的是const Date*,属于权限放大。因为无法在参数中修饰this指针,所以C++的做法就是把const放在函数名之后作为this的修饰。
void Print() const { cout << _year << "年" << _month << "月" << _day << "日" << endl; }
并且,只读函数加上const,读写分离能够更好的保护数据。
结尾👍
以上便是六大成员函数的介绍,如果有疑问或者建议都可以私信笔者交流,大家互相学习,互相进步!🌹