文章目录
- 构造函数
- 析构函数
- 拷贝构造函数
- 构造和析构的执行顺序
- 思维导图
本文主要介绍类中自动生成的构造函数,析构函数,构造拷贝函数。
构造函数
对于上述日期类来说,每次创建类对象的时候都需要调用初始化函数,那能不能再创建的时候就初始化完毕了呢。
构造函数是一个特殊的成员函数,它有以下几个特点:
- 函数名与类名相同
- 无返回值
- 对象实例化时,自动调用
- 支持重载函数
这里要注意构造函数与初始化函数功能类似,并不是开辟空间的作用。如上图,我们在d1这个对象实例化的时候自动调用的构造函数,实现了赋值操作。
那如果我们不自己定义呢?
如上图我们发现成员变量为随机值。
下面的代码运行结果是什么呢?
运行结果:
我们可以看到,我们的年份那些变量并没有初始化,但是我们的 _t 却被调用了。
这里要说一下,C++的类型分为两种:
- 内置类型:int,double等等
- 自定义类型:class/struct等等
C++标准定义了当我们没有显式定义构造函数的时候,对于内置类型是否初始化时是没有规定的,所以这完全依靠编译器决定,而自定义类型必须初始化,如上面的 _t,它会调用 _t 的构造函数来初始化_t。当然,如果我们 Time类中并没有初始化 _a变量,那么这个内置类型也不会初始化的。
如上图:C++11中添加了一个补丁,那就是在写成员变量的时候,可以使用缺省参数,这样就可以让成员变量初始化了。
析构函数
默认生成的清理函数(主要是清理资源)
特点:
- 函数名就是类名,只不过名字前面加了个 ‘~’ 符号(按位取反符号)
- 无参数
- 不支持函数重载
- 对象声明周期结束后,会默认调用析构函数
当我们运行到return 0的时候,按下f11直接进入到析构函数。
拷贝构造函数
特性:
- 拷贝构造函数是构造函数的一个函数重载
- 拷贝构造函数只有一个参数,那必须是类的类型的引用,如果不是的话会无限递归。
- 拷贝构造函数没有显式定义的时候,会生成一个默认的拷贝构造函数,不过这里的只能进行值拷贝 - C++也叫浅拷贝。
先来谈一谈第二点为什么需要传类的引用:
如上图:语法规定:当自定义类型传值传参的时候,会默认调用拷贝构造函数,如果我们的拷贝构造函数参数是 Data 类型,那么还会继续调用拷贝构造函数,解决方法就是用类的类型引用或者参数用指针接受,但是用指针接受就不是拷贝构造函数了。
当我们显式定义了拷贝构造函数之后,我们创建d2对象之后,会默认调用我们的拷贝构造函数。那如果我们不写呢?
我们发现,即使我们不自己定义,编译器默认生成的也可以,那我们就不需要自己写了吗?来看一下我们的栈:
- 可以看到,当我们没有写拷贝构造函数的时候,编译器默认生成的为浅拷贝,它是直接把st1的地址拷贝给st2,所以当我们运行结束后调用析构函数就调用了两次,不过这里是先调用的st2的析构函数(后面再说),所以当我们的st1调用析构的时候,此时我们的st1的_a和st2的_a是同一快空间,我们再free一遍肯定要报错的,free了一片已经free得空间,就相当于下面这种情况[doeg]:
所以当我们有想 stack的 _a,这种资源类型时(动态开辟等等),我们就必须自己写一个拷贝构造函数了。(深拷贝)
深拷贝实现:
//这里加const的原因就是,防止写错,例如把类里面的值拷贝到st里面(写反了)Stack(const Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (_a == nullptr){printf("malloc fail!\n");exit(-1);}memcpy(_a,st._a,sizeof(int)*(st._size));_size = st._size;_capacity = st._capacity;}
构造和析构的执行顺序
构造函数的执行顺序就是你先写谁先执行。
析构函数的执行顺序则是:先局部你后写的反而先析构,然后再是全局的,全局也是后写的先析构
析构顺序:
注意:析构的顺序,当局部有静态类的时候,它的生命周期是到程序结束的时候才结束,所以即使他存在局部域,也是在程序结束的时候才调用析构的。