C++基础概念(三)
前言
`本文主要介绍类的几个默认成员函数,有默认构造函数,析构函数,拷贝构造和运算符重载,以日期类为例说明
一、构造函数
什么是构造函数,有什么价值,使用需要注意哪些事项?
首先构造函数是一个特殊的成员函数,名字与类相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象生命周期内只调用一次。
默认构造函数有三种
- 无参的
- 全缺省或半缺省的构造函数
- 我们不写编译器默认生成的
简单来说,为了确保我们在实例化类时,尽量选择自己写的全缺省的默认构造函数,这样构造出来类的初始值可以保证。
特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
#include<iostream>
using namespace std;
class Date
{
Date(int year=1,int month=1,int day=1)
{
_year=year;
_month=month;
_day=day;
}
}
一般主要的疑惑点在于如果我们不写,编译器会生成默认的构造函数,但是看起来构造函数又没什么用?依然生成的是随机值,那默认的构造函数的价值在哪里?
对于类似的问题,考虑的出发点是编译器生成的默认构造函数对于内置类型和自定义类型的处理方式不一样,对于内置类型如果无参,则会是默认值,对于自定义类型,编译器生成的默认构造函数会对自定义类型调用它的默认成员函数,如果没有则会报错。
二、析构函数
什么是析构函数,它有什么作用,使用虚构函数需要注意什么事项?
首先同样滴 虚构函数也是我们不写编译器会自动生成的默认成员函数,局部对象销毁工作由编译器完成,对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
特点:
- 无参,无返回值
- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
#include<iostream>
using namespace std;
class Date
{
~Date()
{
cout<<"~Date()"<<endl;
}
}
析构函数对于浅拷贝的类默认生成的可以正常使用,对于链表,队列等涉及到深拷贝的类类型,编译器自动生成的析构函数不可用,因为会对同一片空间释放两次,相当于对空指针释放,则会报错
三、拷贝构造函数和运算符重载
什么是拷贝构造函数,有什么作用和特点,使用时需要注意哪些事项
简单来说,我们的拷贝构造就是当一个类准备实例化时,用现有的类去赋值给他;
构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象,创建新对象时由编译器自动调用。
特点:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
另默认的拷贝构造函数对象会按内存存储按字节序完成拷贝,这样的拷贝我们称之为浅拷贝
对应的深拷贝一般来说,编译器自己生产的默认拷贝构造不管用,需要我们自己实现。
运算符重载主要是为了增加C++代码的可读性而新增的功能,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字和参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
特别注意:
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现
四、赋值运算符重载
特点:
- 参数类型
- 返回值
- 检测是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2018,10, 1);
d1 = d2;
return 0;
}
主要区分拷贝构造和赋值的区别在于左值是不是已经存在还是准备实例化,如果已经存在则是赋值,如果是类的实例化则是拷贝构造