【蚕食C++】构造函数&析构函数

为什么需要构造函数?
面向对象的思想,生活中存在的对象都是被初始化后入世的,初始状态是对象普遍存在的一个状态。
当设计一个类时,如果类的内部声明了某些变量,但是没有赋值,当创建这个类的对象时,已经将这些变量的空间开辟出来了,但这些没有正确数据的变量空间是危险的。我们要想给这些变量赋值只能通过init()方法,把具体的值传进去。

然而,在下图中的位置,这段时间内,这个变量空间依然是危险的。如果是开启了多线程,t1的值就可能被调用,那么t1的值就是未知的,且危险的。所以,才有了构造函数。

在这里插入图片描述
总结一下,如果不用构造函数初始化,该怎么办?:
1)为每个类都提供一个public的init()函数;
2)对象创建后立即调用你init()函数进行初始化。

缺点:
1) init()函数只是一个普通函数,必须显示的调用;
2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的,没有初始化的对象,其内部成员变量的值是不定的。


1 构造函数

C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数
在这里插入图片描述
在对象被创建的时候,用来初始化对象的函数。同样,构造函数可以进行重载。可以有无参构造函数


2 析构函数
  • 析构函数和构造函数都没有返回值

  • 析构函数没有形参

  • 析构函数不能重载

    在一个对象被销毁之前,会自动调用析构函数。

    析构函数为什么会出现?
    在这里插入图片描述 比如上图,一个类的private中声明的变量,但是如果,声明了一个指针类型的变量,但是指针变量是在上开辟了多少字节的空间。但是当自动调用析构函数时,销毁private中声明变量所占的空间。但是char *指针所指向的堆空间却无法销毁了
    因此,就需要析构函数,在析构函数中将堆上的空间释放,那么就不需要自己去管理其释放,当对象销毁时,就会自动由析构帮助你释放

【注意】:如果不设置构造函数,系统就会自动构造一个无参构造函数,如下:空的函数体
在这里插入图片描述
显式提供一个构造函数(显式的无参构造、显式有参、显式的拷贝构造),默认的构造函数就不存在了

3 拷贝构造函数

对于同一个类,我们创建两个不同的对象,用一个对象初始化另一个对象时,会涉及拷贝构造函数。

类存在一个默认的拷贝构造函数。

1)显示的拷贝构造函数
即,通过同一个类的另外一个对象来初始化自己。
在这里插入图片描述
2)下面这个不是拷贝构造函数
在这里插入图片描述
t3的构造函数在对象初始化时调用,这里调用的是无参构造函数
第二行调用的是=赋值操作符
在这里插入图片描述
【注意】Test t3(t1);与Test t3=t1;都是调用t3的拷贝构造函数。两者是相同的。

3)析构函数调用的顺序
谁先构造,谁后析构,因为栈是先进后出的 。
在这里插入图片描述
4)下图的函数调用,传入的形参是调用的拷贝构造函数
在这里插入图片描述
5)函数返回值是一个类对象
函数返回一个匿名的对象。
当一个函数返回一个匿名对象的时候,函数外部没有任何变量去接收它,这个匿名对象将不会再被使用(找不到),编译器会直接将这个匿名对象回收掉,而不是等待整个函数执行完毕再回收
在这里插入图片描述
对于上面返回的匿名对象,下面这个函数体使用了一个t1进行接收,那么这里是否进行了两次拷贝呢
不会。这里拷贝依然发生在被调用函数返回前的那一刻,当进行到下面的t1=func2()时,仅仅是将返回的匿名对象转正,将这个匿名对象起了一个名字t1
在这里插入图片描述
如果是下面这种情况,t1已经初始化了,后面的t1=func2(),已经不是拷贝操作,而是赋值,因此,func2返回的匿名对象会被立即回收掉。
在这里插入图片描述

4 深拷贝与浅拷贝

只要类中涉及指针变量,就需要显示的提供一个拷贝构造函数,来完成深拷贝动作
如果在构造函数时,使用一个对象t1给另一个对象t2进行初始化,那么就会触发对象t2的拷贝构造函数。
这时候,如果t1中存在两个变量m_id和char *类型的指针,使用默认的拷贝构造函数就会存在一个问题。
因为拷贝构造函数是直接赋值的,所以t2的指针变量会直接等于t1的指针值,即两个指针变量指向同一块地址那么必然存在其中一个对象在析构时无法析构,因为已经被析构了。
在这里插入图片描述
这时候,就需要显示的提供一个拷贝构造函数,来完成深拷贝动作。
在这里插入图片描述
同样适用于赋值操作

5 构造函数的初始化列表

在这里插入图片描述
上面的写法是错误的,这不是初始化m_a1,而是赋值操作;
对于一个类对象成员,想要初始化,需要写在初始化列表中
在这里插入图片描述
构造对象成员的顺序跟初始化列表的顺序无关,而是跟成员对象的定义顺序有关

【注意】:const修饰形成的常量,一定要放在初始化列表的初始化

6 类中包含另外一个类

在这里插入图片描述
如上图,类ABCD中包含类ABC,那么m_abc是一个对象,不是一个普通的值。那么它怎么构造?
我们可以在ABCD的构造时,触发ABC的构造函数。注意:不可以在ABCD的构造函数体中进行触发。可以在初始化列表中触发

函数传参发生拷贝构造操作。

发布了57 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43343803/article/details/104605285