文章目录
1.类的六大默认成员函数2.构造函数2.1 默认构造函数2.2 显式调用构造函数 3.析构函数3.1 默认析构函数3.2 显式调用析构函数 希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力!本篇将介绍类和对象中的重点部分,六大函数中的前四个尤为重要,所以这部分篇幅或许会有点长,所以请各位读者们耐心看完,相信看完会有不一样的收获?
1.类的六大默认成员函数
一个类里面如果什么都没有
,就被叫做空类
,但是空类里并不是真的什么都没有,而是存在默认构造函数
。这个默认构造函数虽然没有做任何具体的初始化操作(因为类中没有成员变量),但它满足了创建对象的基本需求
默认成员函数:用户没有显式实现
,编译器会生成的成员函数称为默认成员函数
• 构造函数:主要完成初始化
工作
• 析构函数:主要完成清理
工作
• 拷贝构造函数:使用同类对象初始化创建对象
• 赋值运算符重载函数:将一个对象的值赋值
给另一个同类型的对象
• 取地址运算符重载函数:返回对象的地址
或返回const对象的地址
• const 取地址运算符重载函数:返回 const 对象在内存中的地址
2.构造函数
构造函数
是特殊的成员函数
,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象
,而是初始化对象
其语法形式为:
类名 () {}
?值得注意的是:
函数名与类名相同
无返回值
对象实例化
时编译器自动调用
对应的构造函数构造函数可以重载
在开空间
和释放空间
特别好用,因为自动调用
,不会忘记一般不能放在私有
(学单逆模式
的时候是放在私有的) 2.1 默认构造函数
默认构造函数
是一种特殊的构造函数
,在没有显式提供初始化值
的情况下,用于创建对象并进行默认的初始化
。它没有参数
或者所有参数都有默认值(全缺省)
,比如使用缺省参数
就是,并且在对象整个生命周期内只调用一次
?为什么要有默认构造函数?
当创建一个对象
时,需要对其进行初始化
。默认构造函数
提供了一种简单的、默认的初始化方式。例如,对于一个包含多个成员变量的类,默认构造函数可以将这些成员变量初始化为合理的默认值
(即使是随机值也会是个确切的值),这样就保证了对象在创建后处于一个确定的、合理的初始状态
。这就像是在建造一个房子时,给它一个基本的初始状态,比如所有的房间都是空的,墙壁是白色的等
?为什么内置类型不用默认构造函数?
内置类型
(如int
、double
、char
等)在 C++ 中是语言本身提供的基本数据类型
,它们的初始化相对简单直接,它们不像类类型对象那样,有复杂的成员变量
和可能需要执行的构造函数
来完成初始化
举个例子:
class MyClass {public: int num; // 默认构造函数 MyClass() { num = 0; }};int main() { MyClass obj; // 此时obj.num的值为0,因为默认构造函数将其初始化为0 cout << obj.num; return 0;}
这里调用的默认构造函数
是无参的
,也可以不写 num = 0
这一条语句,只不过 num 的值就变成随机值
了,也就是说如果要对对象进行操作一定要有默认构造函数显式存在
,无论你赋不赋值,至少要让 num 有个值保证其处于一个确定的状态
;反过来如果只是创建了对象
的话,没有进行对象操作,那么可以不显式写默认构造函数
,编译器会自动生成一个无参的默认构造函数
?值得注意的是:调用构造函数不传参数
时不能写成MyClass obj()
,只能写成 MyClass obj
,因为对象后面不能跟括号
,否则就成了函数声明
;无参的
,全缺省的
,我们没写编译器默认生成的默认构造函数
只能有一个
2.2 显式调用构造函数
显式调用构造函数
主要用于精确控制对象的创建和初始化过程
,当一个类有多个构造函数
,且参数类型可能存在转换关系时,显式调用构造函数可以明确指定使用哪一个构造函数来创建对象
,也就是带参的构造函数
举个例子:
class Date{public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; }private: int _year; int _month; int _day;};int main() { Date d1(2024, 12, 6); return 0;}
传入参数2024、12 、 6,将 d1 对象初始化为表示 2024 年 12 月 6 日这个日期
?值得注意的是:当在类中显式定义了其他构造函数(非默认构造函数)时,编译器仍然会生成默认构造函数
,但是优先使用
显式定义的构造函数
3.析构函数
析构函数
是类的一个特殊成员函数,与构造函数功能相反
,析构函数不是完成对对象本身的销毁
,对象销毁工作是由编译器完成的
。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
。对象在生命周期结束时,需要释放其占用的资源,以避免资源浪费
和可能出现的内存泄漏
等问题
其语法形式为:
~类名 () {}
?值得注意的是:
析构函数名是在类名前加上字符 ~
无参数无返回值类型
一个类只能有一个析构函数
。若未显式定义,系统会自动生成默认的析构函数
,注意:析构函数不能重载
对象生命周期结束
时,C++编译系统系统自动调用析构函数
3.1 默认析构函数
对于只包含基本类型
(如int
、double
、char
等)成员变量的类,默认析构函数虽然看起来没有做什么实际的操作,但它是整个对象销毁过程的一部分
举个例子:
class Time{public:~Time(){cout << "~Time()" << endl;}private:int _hour;int _minute;int _second;};class Date{private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;};int main(){Date d;return 0;}
程序运行结束后输出:~Time()
,在 main 中根本没有直接创建 Time 类的对象,为什么最后会调用 Time 类的析构函数?
内置类型
成员,销毁时不需要资源清理
,最后系统直接将其内存回收
即可;而 _t
是Time
类对象,所以在 d
销毁时,要将其内部包含的 Time
类的 _t
对象销毁
,所以要调用 Time
类的析构函数
。但是:main 函数中不能直接调用
Time
类的析构函数,实际要释放的是 Date
类对象,所以编译器会调用 Date
类的析构函数
,而Date
没有显式提供,则编译器会给 Date
类生成一个默认的析构函数
,目的是在其内部调用 Time
类的析构函数,即当 Date
对象销毁时,要保证其内部每个自定义对象都可以正确销毁
?总结:main 函数中并没有直接调用Time 类析构函数,而是显式调用编译器为 Date 类生成的默认析构函数
,创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
3.2 显式调用析构函数
当使用 malloc
在预先分配好的内存空间中创建对象时,就需要显式调用析构函数
来进行对象销毁
#include <iostream>class Array {private: int* data; int size;public: // 构造函数,用于开辟指定大小的内存空间 Array(int n) { size = n; int* data = (int*)malloc(sizeof(int) * n); } // 析构函数,用于释放构造函数中开辟的内存空间 ~Array() { free(data); data = nullptr; }};int main() { Array arr(5); return 0; }
这段代码自动调用
构造函数和析构函数实现了空间创建和销毁
,避免忘记
释放或开辟空间
?值得注意的是:
一般情况下有动态资源申请
,就需要显式写
析构函数释放资源没有动态申请
,不需要写析构函数需要释放的成员都是自定义类型
,不需要写析构函数,这些成员变量所属类的析构函数会自动被调用