【类与对象 -2】学习类的6个默认成员函数中的构造函数与析构函数

2024-02-17 18:36

本文主要是介绍【类与对象 -2】学习类的6个默认成员函数中的构造函数与析构函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.类的6个默认成员函数

2.构造函数

2.1概念

2.2特性

3.析构函数

3.1析构函数的概念

3.2特性


1.类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
函数。
默认成员函数: 用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

下面我们来分别学习一下这6个函数~

2.构造函数

2.1概念

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Init(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}

在日常使用时,我们可能会忘记对对象进行初始化,程序报错后反而检查半天才发现问题。在为了避免这种失误出现,C++中引入了构造函数。

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2特性

构造函数在函数名上看起来好像是创建对象,实际上是完成对象的初始化

构造函数特征如下:

①函数名和类名相同;

②没有返回值,不是在前加void,而是压根不需要写;

③对象实例化时编译器自动调用对应的构造函数;

④构造函数可以重载;

上面代码的构造函数形式如下:

(1)不带参数的构造函数,由于编译器自动调用构造函数,在定义对象时就已经初始化了,

(2)带参数的构造函数,在定义对象的时候就要将初始化的值加在后面。

调用带参的构造函数时在括号内加入要初始化的值跟在对象后,那调用无参的构造函数时可以直接跟括号在对象后吗?(为什么调用无参的构造函数时后面不用加括号)

不能。这种形式就成了函数声明。

如下图,声明了一个d3函数,该函数没有参数,返回一个Date类型对象。

以理论上两种形式可以同时存在,构成了函数重载,但是一般情况下我们不会同时写出这两个,因为调用存在歧义。

这两种形式可以合为一种,利用我们之前学过的缺省参数来修改,如下面图中的形式:

⑤如果在类中忘记显式定义构造函数也不用担心,C++编译器会自动生成一个无参的默认构造函数。

我们忘记定义构造函数时执行程序,发现可以正常运行,这是因为编译器会自动生成一个默认构造函数,但是看运行结果截图我们可以发现编译器没有对参数进行初始化,_year、_month、_day都是随机值。那么编译器生成的默认构造函数有什么用呢?

C++中把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,像int/char/double/指针等;自定义类型就是我们使用的struct、class等自己定义的类型。

C++98中规定对默认生成的构造函数对内置类型不作处理,对自定义类型则调用它的默认构造函数。如果自定义类型没有默认构造函数就会报错。

其实自定义类型的尽头就是内置类型。

以上Date类中的_year、_month、_day就是int类型,所以不作处理,是随机值,接下来我们验证一下自定义类型。

•有些编译器可能会对内置类型进行处理(int初始化为0,double初始化为0.0,指针初始化为nullptr),但C++标准并没有规定内置类型要处理。

C++11中对内置类型不处理的缺陷又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

分析一个类型成员和初始化要求,如果需要些构造函数我们就自己写,不需要时就靠编译器自动生成。结论:大多情况下都需要自己实现构造函数

⑥无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。

不止是编译器生成的构造函数叫默认构造函数,我们自己显式定义的无参构造函数,全缺省构造函数都是默认构造函数。即不需要传参就能调用的构造函数,就叫默认构造函数

默认生成的构造函数不能与其他两种同时存在;无参构造函数不能与全缺省构造函数同时存在,会产生调用歧义。

一般情况下,我们建议使用全缺省构造函数

3.析构函数

3.1析构函数的概念

前面我们学习了对对象进行初始化构造函数(类似于栈中的初始化函数Init),那么怎样清理资源呢,这就要提到我们接下来要学习的析构函数(栈中的销毁函数Destory)。

析构函数也是一种特殊的成员函数,与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

3.2特性

析构函数特征:

①析构函数名是在类名前加上字符 ~。(C语言中“~”作用是按位取反,析构函数前加表示该函数与构造函数作用相反)
②无参数无返回值类型。
③一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载
默认生成的析构函数与构造函数类似,对内置类型不作处理,自定义类型则调用它的析构函数。
(Date类没有显式定义析构函数,系统将自动生成,Date类中定义了自定义类型Time类型的对象t,调用它的析构函数~Time()。)
对象生命周期结束时,C++编译系统系统自动调用析构函数。
以下面的日期类为例,定义一个析构函数~Date()。(其实日期类的析构函数没什么有用的,因为它的_year等都是属于对象,并没有额外开辟空间,也就没有什么资源需要清理,此处只是用来验证它的特性)
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << "~Date()" << endl;}
private:int _year;int _month;int _day;
};
void func()
{Date d2;
}
int main()
{func();Date d1;d1.Print();return 0;
}

如果类中没有申请资源时,析构函数可以不写 ,直接使用编译器生成的默认析构函数,比如
Date类; 有资源申请时,一定要写, 否则会造成资源泄漏,比如Stack类。

如果我们写了一个栈,忘记写析构函数会发生内存泄漏。虽然函数结束栈帧销毁,但是在堆上额外开辟了一块空间没有释放。C语言中避免出现内存泄漏需要在函数结束前调用Destory函数来销毁,但是这种方法在使用时极有可能忘记,因而C++中引入了析构函数。

class Stack
{
public:Stack(size_t capacity = 4){_array = (int*)malloc(sizeof(int) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(int data){// CheckCapacity();_array[_size] = data;_size++;}
private:int* _array;int _capacity;int _size;
};
int main()
{Stack S;return 0;
}

对以上代码加上析构函数,结果如下

**先定义的先调用构造函数,后定义的先调用析构函数

class Date
{
public:Date(int year){_year = year;cout << "Date()->" << _year << endl;}~Date(){cout << "~Date()->" << _year << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};
Date d5(5);
static Date d6(6);
void func()
{Date d4(4);
}
int main()
{Date d1(1);static Date d2(2);Date d3(3);func();return 0;
}

分析上面代码,猜测结果是什么?

我们先分析一下各个对象是全局还是局部变量。d1、d2、d3、d4是局部变量,d4是在函数 func() 内定义,函数 func() 在main函数内调用,所以d4的生命周期先结束。d2又被static修饰(称为静态局部变量),改变了它的存储位置,使得它的生命周期变长,直至程序结束,生命周期才结束,在销毁之前应先将main函数内的局部变量先销毁(后定义先析构)再销毁该静态局部变量。d5、d6是全局变量,虽然d6被static修饰(静态全局变量),但它与全局变量的存储位置没有区别,生命周期仍是整个程序的执行期间,也是后定义的先析构。

所以调用析构函数的顺序是d4-d3-d1-d2-d6-d5

结果如下:

总结:

调用析构函数的顺序:局部对象(后定义的先析构)->静态局部对象->全局对象(后定义的先析构)

        构造函数和析构函数的学习就到这了,下篇我们将一起学习其他的默认成员函数,谢谢观看!

这篇关于【类与对象 -2】学习类的6个默认成员函数中的构造函数与析构函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/718663

相关文章

golang 对象池sync.Pool的实现

《golang对象池sync.Pool的实现》:本文主要介绍golang对象池sync.Pool的实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收的压力,下面就来介绍一下,感兴趣的可以了解... 目录sync.Pool的用法原理sync.Pool 的使用示例sync.Pool 的使用场景注意sync.

PyTorch中cdist和sum函数使用示例详解

《PyTorch中cdist和sum函数使用示例详解》torch.cdist是PyTorch中用于计算**两个张量之间的成对距离(pairwisedistance)**的函数,常用于点云处理、图神经网... 目录基本语法输出示例1. 简单的 2D 欧几里得距离2. 批量形式(3D Tensor)3. 使用不

SpringBoot项目中Redis存储Session对象序列化处理

《SpringBoot项目中Redis存储Session对象序列化处理》在SpringBoot项目中使用Redis存储Session时,对象的序列化和反序列化是关键步骤,下面我们就来讲讲如何在Spri... 目录一、为什么需要序列化处理二、Spring Boot 集成 Redis 存储 Session2.1

Java实例化对象的​7种方式详解

《Java实例化对象的​7种方式详解》在Java中,实例化对象的方式有多种,具体取决于场景需求和设计模式,本文整理了7种常用的方法,文中的示例代码讲解详细,有需要的可以了解下... 目录1. ​new 关键字(直接构造)​2. ​反射(Reflection)​​3. ​克隆(Clone)​​4. ​反序列化

MySQL 字符串截取函数及用法详解

《MySQL字符串截取函数及用法详解》在MySQL中,字符串截取是常见的操作,主要用于从字符串中提取特定部分,MySQL提供了多种函数来实现这一功能,包括LEFT()、RIGHT()、SUBST... 目录mysql 字符串截取函数详解RIGHT(str, length):从右侧截取指定长度的字符SUBST

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

C++类和对象之初始化列表的使用方式

《C++类和对象之初始化列表的使用方式》:本文主要介绍C++类和对象之初始化列表的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C++初始化列表详解:性能优化与正确实践什么是初始化列表?初始化列表的三大核心作用1. 性能优化:避免不必要的赋值操作2. 强

Kotlin运算符重载函数及作用场景

《Kotlin运算符重载函数及作用场景》在Kotlin里,运算符重载函数允许为自定义类型重新定义现有的运算符(如+-…)行为,从而让自定义类型能像内置类型那样使用运算符,本文给大家介绍Kotlin运算... 目录基本语法作用场景类对象数据类型接口注意事项在 Kotlin 里,运算符重载函数允许为自定义类型重

Pandas中统计汇总可视化函数plot()的使用

《Pandas中统计汇总可视化函数plot()的使用》Pandas提供了许多强大的数据处理和分析功能,其中plot()函数就是其可视化功能的一个重要组成部分,本文主要介绍了Pandas中统计汇总可视化... 目录一、plot()函数简介二、plot()函数的基本用法三、plot()函数的参数详解四、使用pl

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen