第二层:对象的初始化和清理

本文介绍了C++中的构造函数和析构函数,包括它们的语法、作用以及分类。构造函数在对象创建时调用,用于初始化对象,而析构函数在对象销毁时调用,负责清理资源。文章还讨论了拷贝构造函数、深拷贝与浅拷贝的概念,并探讨了初始化列表和静态成员的使用。
摘要由CSDN通过智能技术生成

🎉welcome🎉
✒️博主介绍:一名大一的智能制造专业学生,在学习C/C++的路上会越走越远,后面不定期更新有关C/C++语法,数据结构,算法,Linux,ue5使用,制作游戏的心得,和大家一起共同成长。
✈️C++专栏:C++爬塔日记
😘博客制作不易,👍点赞+⭐收藏+➕关注

前情回顾

上回说到,我踏入C++古塔,在第一层中了解到了面向对象的第一大特性——封装,通过努力,我成功获得封装的力量,并且上到了第二层…

对象特性——对象的初始化和清理

踏入第二层当中,那道声音再次飘来:“挑战者,这层的任务需要你掌握对象特性中的初始化和清理,祝你好运…”一块石板再次出现在我的眼前

构造函数和析构函数

什么是构造函数和析构函数

在C++中,对象在被调用和销毁的时候,会默认调用两个函数,在创建对象的时候,会调用构造函数,在销毁的时候会调用析构函数,它们在一个对象中,只会被调用一次,所以构造函数和析构函数是必须存在的,当程序员本身不提供任何的构造和析构函数的时候,编译器会提供默认构造函数

构造函数

构造函数的语法
  • 构造函数没有返回值,也不用写void
  • 构造函数的函数名与类名相同
  • 构造函数可以有参数,可以发生函数重载1

基本语法了解,那构造函数就可以尝试的去写一下。

class A
{
public:
	A()
	{
		cout << "A的构造函数" << endl;
	}
	string a;
};
int main()
{
	A c;
}

在这里插入图片描述

构造函数的分类
按照参数分类

按照参数可以分为:无参构造函数(默认构造函数)和有参构造函数,它们的区别在于函数有无参数。

按照类型分类

按照类型可以分为:普通构造函数和拷贝构造函数。

拷贝构造函数

上面提到了拷贝构造函数,那拷贝构造函数是什么?从表面来看,在构造的同时拷贝,那拷贝什么?数据吗?拷贝到哪里去呢?这里我们先将拷贝构造函数的语法写罗列出来。

  • 类名(const 类名 引用2

可以看到,它的参数是我们的对象,那它的作用大致就可以猜到了,就是将对象的数据拷贝到调用拷贝构造函数的对象上面去,可以用代码来试验一下。

#include<string>
#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A的构造函数" << endl;
	}
	A(A &c)
	{
		b = c.b;
	}
	int b;
};
int main()
{
	A c;
	cin >> c.b;
	A b(c);
	cout<< b.b << endl;
}

在这里插入图片描述
可以看到c里面的内容拷贝到了b里面。

拷贝函数的调用时机

通常在三种情况下调用我们的拷贝函数:

  1. 使用一个已经创建完毕的对象来初始化一个新对象,可以参考上面
  2. 值传递的方式给函数参数传值
  3. 以值的方式返回局部变量

对于第二点,可以看下方这张图来理解在这里插入图片描述
int main函数内部调用test1函数,函数参数为类,在调用函数时候,会调用拷贝构造函数,然后在回去调用test1函数内部。
而第三点,是因为函数中,当返回的值是我们的对象时,因为出了函数体,局部变量销毁,所以函数内对象会进行销毁,这个时候就会发生拷贝,将函数内的对象数据重新拷贝到一块新的空间内,进行返回。

深拷贝和浅拷贝
  • 浅拷贝是进行简单的拷贝操作,只会进行值拷贝
  • 深拷贝是在堆区重新申请空间,进行拷贝

那为什么会出现深拷贝和浅拷贝呢?当类内的属性有指针的时候,这个时候发生拷贝,会拷贝的是地址。
在这里插入图片描述
因为指针内存放数据之前,我们要使用new3函数进行开辟空间,这个时候我们就需要在析构函数内使用delete4释放空间,但是因为两个对象都同时指向了同一块空间,就会释放两个同一块空间的地址,对堆区进行了重复释放,那这个时候想要解决,就需要程序员自己设计出拷贝构造函数,进行深拷贝,对指针的拷贝用new操作符重新开辟一块空间。
深拷贝

class A
{
public:
	A()
	{
		cout << "A的构造函数" << endl;
	}
	//浅拷贝
	A(A &c)
	{
		b=c.b;
		d=c.d;
	}
	//深拷贝
	A(A &c)
	{
		cout << "拷贝构造函数" << endl;
		b = c.b;
		d = new int(*c.d);
	}
	int b;
	int* d;
};
构造函数的调用方法
括号法

括号法在上面我们进行拷贝构造函数调用时已经用过,就是在对象后面加括号,括号内是参数,但是这个时候要注意!对于无参构造函数而言,是在后面加括号的,这样编译器会认为这一个函数声明在这里插入图片描述
在编译器眼中,加括号的就会认为是这样。

显示法

显示法是将我们的构造函数当赋值一样,书写方式如下:

类名 对象名 = 类名(参数);

函数名(参数)叫做匿名对象,特点是当执行结束后,系统会立即回收匿名对象。
注意!:不要利用拷贝构造函数去初始化匿名对象,这个时候编译器会认为这是一个函数声明。

隐式转换法

隐式转换法是将我们的显示法中等号右侧的类名省略,书写方式如下:

类名 对象名 =参数;

构造函数规则

在默认情况下,C++编译器至少会给一个类添加三个函数:

  1. 默认构造函数(无参)(内部空实现)
  2. 默认析构函数(内部空实现)
  3. 默认拷贝构造函数,对属性进行浅拷贝

构造函数规则如下:

如果程序员定义有参构造函数,C++不在提供默认构造函数(无参),但是会提供默认拷贝构造函数,如果程序员定义拷贝构造函数,C++则不会提供任何构造函数。

析构函数

析构函数的语法
  • 没有返回值,也不需要写void
  • 函数名和类名相同,但是需要在名称前加~
  • 析构函数不可以有参数,不能发生重载
class A
{
public:
	A()
	{
		cout << "A的构造函数" << endl;
	}
	//析构函数
	~A()
	{
		
	}
	string a;
};
int main()
{
	A c;
}
析构函数的作用

当类内调用new操作符开辟空间时,需要在析构函数内部进行释放空间的操作,用delete操作符,最好在将指向new操作符开辟的空间的指针,置空,防止野指针。

初始化列表

作用

对类内的属性进行初始化操作

语法

构造函数():属性1(初始值),属性2(初始值)…

class man
{
public:
	//构造函数,初始化列表
	man() :_name("狗蛋"), age(18), car("鬼火")
	{

	}
	
	//属性
	string _name;//名字
	int age;//年龄
	string car;//汽车
};

这是最基础的用法,还可以和有参构造函数进行结合:

#include<string>
#include<iostream>
using namespace std;

class man
{
public:
	//构造函数,初始化列表
	man(string a, int b, string c) :_name(a), age(b), car(c)
	{

	}
	
	//属性
	string _name;//名字
	int age;//年龄
	string car;//汽车
};
int main()
{
	man m1("狗蛋", 18, "鬼火");
	return 0;
}

也可以这样去进行初始化。

类对象作为类成员

在C++中,类中的成员可以是另一个类的对象,这个时候称这个成员为对象成员:

class A
{

};
class B
{
	A a;
};

上述代码中,A a就是对象成员,在我们创建B类的对象时,A a为对象成员,要先有成员才能有这个类,所以会先调用A的构造函数,在调用B的构造函数,但是在解析的时候时相反的,先析构B,在析构A。

静态成员

静态成员变量

静态成员变量,是将我们的成员变量放入到静态区,需要在成员的类型前面加static关键字。

静态成员变量特点
  1. 因为放入到了静态区,所以所有对象是共享同一份数据的,不属于任何的一个对象了
  2. 会在编译阶段就分配内存,相当于在运行之前就已经分配好内存了,分配在全局区
  3. 类内声明,类外初始化,比如有一个初始值才可以用,或者就无法访问到那片内存空间。

对于静态成员变量的初始化我们可以这样:

class A
{
	static int a;
}

int A::a=//初始值;

也是因为上述特点,静态成员不属于任何对象,所有有了两种访问方式:

  1. 通过对象进行访问,访问方式:对象名.成员名
  2. 通过类名进行访问,访问方式:类名:: 成员名

静态成员变量也是遵循访问权限的。

静态成员函数

访问方式

与静态成员变量的访问方式是一样的。

注意事项
  1. 静态成员函数可以访问静态成员变量
  2. 静态成员函数不可以访问非静态的成员变量
  3. 放访问非静态的时候,这个时候静态成员函数是不知道修改的是哪个对象的,如果修改,它会把所有对象的这个属性进行修改

静态成员函数同样遵循访问权限。

掌握对象初始化和清理,步入第三层

随着面前石板的倒下,第三层的楼梯显现在了我的眼前…

本章知识点(图片形式)

在这里插入图片描述

😘预知后事如何,关注新专栏,和我一起征服C++这座巨塔
🚀专栏:C++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉

出现的陌生名词解释


  1. 看到函数重载,可能有些人就懵了,这里解释一下,在C++中,函数名是可以重复的,这个时候参数不同,或者参数的顺序不一样,都是可以的,但是如果光返回类型不同,参数相同,则不能发生函数重载,编译器会报错。 ↩︎

  2. 引用,换一种说法就是取别名,比如,我有一个同学,他叫做李鑫,那我们可能会给他取个外号叫做李三金,当说到这个外号的时候,我们会想到李鑫这个人,所以引用是不会在去内存当中重新开辟一块空间的,它和它引用的变量是用了同一块空间,它的书写形式为:
    数据类型 &变量名=要引用的变量名;
    还要注意,对于引用来说,我们不能对其进行第二次改变,如果aa是a的别名,那aa就不能在改变成别的变量的别名了,对于引用来说,引用就等于我们的指针加const,,把const加在我们的解引用右侧就等于我们的引用。 ↩︎

  3. new操作符是C++用于内存开辟的函数,它的书写形式为:
    指针=new 和指针同类型 (这里可以直接赋值);
    指针=new 和指针同类型 [开辟多少个];
    上面第一种为开辟一个变量空间,第二个为开辟数组空间。 ↩︎

  4. delete操作符,用于对new申请下来的空间进行空间释放,书写形式:
    delete(指针); ↩︎

评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

封心锁爱的前夫哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值