1. 首先得知道什么是默认成员函数
所谓默认成员函数就是用户没有显示实现该成员函数,编译器会替用户实现一份,没有显示定义就是没有自己实现
2. 其次要知道默认成员函数有哪些
任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数
3. 下面我将对以上6个默认成员函数做详细解释
(1)构造函数
作用:创建对象,初始化成员变量
构造函数调用时机:当创建对象时,由编译器自动来进行调用,并且在对象的生命周期内只调用一次
特点:
a. 函数名与类名相同
b. 无返回值
c. 可以重载
注意:
a. 当调用无参构造函数创建对象时,对象之后一定不能跟(),否则就成为函数声明,而不是创建对象
b. 若没有显式定义构造函数,编译器默认生成无参的默认构造函数
c. 无参构造函数和全缺省的构造函数都称为缺省构造函数,且只能存在一个
// 以日期类为例
#include <iostream>
using namespace std;
class Date
{
public:
// 若没有显式定义构造函数,编译器默认生成无参的默认构造函数
Date()
{}
// 重载构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1; // 调用无参构造函数
Date d2 (2020, 11, 7); // 调用带参的构造函数
// 当调用无参构造函数创建对象时,对象之后一定不能跟(),否则就成为函数声明,而不是创建对象
Date d3();
}
构造函数还有一个更重要的作用,那就是初始化成员变量
初始化列表:是成员变量定义的地方
哪些变量需要初始化:
a. 引用:引用在定义时必须要初始化,在初始化列表中定义
b. const成员变量必须在初始化列表中初始化
c. 没有默认构造的自定义成员必须在初始化列表中初始化
d. 如果自定义类型有默认构造,编译器会在初始化列表中自动调用
e. 如果成员变量全为内置类型,没有在初始化列表初始化,初始值为随机值
初始化顺序:成员变量在类中的声明次序就是在初始化列表中的初始化顺序
初始化次数:每个成员变量在初始化列表中只能出现1次
// 成员变量初始化
Date(int year, int month, int day):
_year(year),
_month(month),
_day(day)
{}
// C++11中新的初始化方式:
class A
{
public:
A(int a = 10):
_a(a)
{}
private:
// 成员变量是声明
// C++11 初始化方式,相当于一个缺省值
int _a = 1;
};
(2) 析构函数
作用:销毁对象,对象删除前的相关清理工作
调用时机:当对象销毁时编译器自动调用
特点:
a. 析构函数名是在类名前加上字符 ~
b. 无参数,无返回值
c. 一个类只有一个析构函数,若为显式定义,编译器使用默认的析构函数
注意:
a. 对象生命周期结束时,C++编译系统系统自动调用析构函数
~Date()
{}
(3) 拷贝构造函数
作用:用已经存在的对象构造新对象,本质上是构造函数的重载
参数比较特殊:参数是对象
场景:用一个已经存在的对象创建一个本身完全相同的对象时,编译器自动调用拷贝构造函数
注意:传引用,传值会无限调用
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
- 传值的情况:无穷递归下去
特性:
a. 拷贝构造是构造函数的一种重载形式
b. 编译器创建对象时自动调用
c. 用对象创建新的对象时自动调用
d. 参数必须为引用类型,最好加上const限制(匿名变量:其实就是一个表达式的结果,是值而不是“变量”)
e. 如果不显式定义,编译器会自动生成
f. 自动生成的拷贝构造完成字节拷贝/内存拷贝/浅拷贝
g. 什么时候拷贝构造必须自己定义:当对象有资源的时候(如果调用默认的拷贝构造函数,在释放的时候会造成二次释放的问题)具体见下图