前情提要,本章涉及的知识点较多,建议收藏慢慢消化
类的6个默认成员函数
但一个函数中什么成员都没有,那么我们就叫它空类
- 但是空类里面真的什么都没有嘛?
- 其实并不是,任何类在什么都不写的时候,编译器会自动生成一下六个默认成员函数
以下为六个默认成员函数👇
一、 构造函数
- 概念
我们在用C写栈的时候,是否经常会遇到初始化的问题而导致程序崩溃。忘记调用 Init 就使用,那么下面的构造函数就能很好的解决我们的问题。
int main()
{
//忘调用Init 就使用
Date d1;
Date d2;
d1.Print();
d2.Print();
}
-
这个时候就很有构造函数的必要了!
-
构造函数可以重载也就代表有多种初始化方式
比如一下两种:
-
让我们来运行一下看看结果:
-
虽然可以构造函数,但是一般情况下,一般建议每个类都可以写一个全缺省函数。
- 特性
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
务并不是开空间创建对象,而是初始化对象。
其特征如下:
1. 函数名与类名相同
2. 无返回值
3. 对象实例化时编译器自动调用对应的构造函数
4. 构造函数可以重载
在这里我们使用日期函数来举例子:
- 此时我们在用main函数实行的时候发现,欸嘿,完成初始化了!
- 在这里,没有调用Init函数,却没有崩!
- 欸?那有一个问题,如果我们不写构造函数,结果又会是什么呢?
糟了…怎么会是随机值呢!
不要慌哈!C++ 在这里给我们的数据类型分为两个部分。
既然这样,那我们加上一个自定义类型尝试一下。
int main()
{
Date d1;
d1.Print();
return 0;
}
- 然后我们又扯出一个非常重要的知识点:默认构造函数
不实现构造函数的情况下,编译器会生成默认的构造函数。
-
问题一 ,当我们这样改动的话,他还是构造函数吗?
这个时候,没有构造函数,编译器就报错啦!
-
总结:不传参数就可以调用默认构造函数:
所以,在上面传参后,Date _t 不具备默认构造函数 -
无参构造函数, 全缺省构造函数, 我们没写编译器默认生成的构造函数,都可以认为是默认构造函数->基本上只能存在一种
对此我们对应的方法就是:给缺省值:就是这样!👇
这个也是C++后期打补丁添加的
- 在这里就是声明, 并不是初始化
总结:
- 一般情况下,构造函数都需要我们自己显示的去实现
- 只有少数的情况下, 可以让编译器自动生成构造函数
二、析构函数
- 概念
对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
-
我们在开辟空间后,常常又会忘记调用 Destory 函数
-
这样的结果往往会导致内存泄漏,析构函数由而诞生
-
像上面的Date函数是不需要析构函数的,因为没有开辟新的空间,故而没有资源需要清理
- 特性
析构函数时特殊的成员函数,其特征如下:
- 析构函数名是在类名之前加上字符 ~
- 无参数无返回值类型
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
- 在函数生命周期结束前自动调用!
- 代码如下👇:
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3) //这个是栈的构造函数
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
// 其他方法...
~Stack() //析构函数
{
cout << "~Stack()" << endl; // 这里打印出是为了更好的看出是否调用
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
欸!我们运行一下,发现析构函数和构造函数都打印出来了,说明该运行过程中调用了该成员函数
- 这里说明了析构函数是可以自动调用的!
在实践中总结:
1. 资源中需要显示清理,就需要写析构,如:Stack List
2. 有两种场景不许呀显示写析构函数,默认生成就可以了
a 没有资源需要清理,如: Date
b 内置类型成员没有资源需要清理,剩下都是自定义类型成员。如: MyQueue
在这里小小的总结一下哈:
- 构造函数就是初始化函数
- 析构函数就是销毁函数
三、拷贝构造函数
- 概念
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)
在用已存在的类类型对象创建新对象时由编译器自动调用
拷贝构造也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
- 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
- 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了
在代码上是这样实现的👇
- 这样是可以进行成功的赋值,但是需要注意的一点是
//当初始化为一下这种形式时,会发生无穷递归
Date(Date d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//为什么呢?
- 知识点:传自定义类型的传值传参需要调用拷贝构造完成
调用func得先传参,自定义类型对象传参要调用拷贝构造函数完成