【C++】对象初始化和清理

2023-10-27

对象的初始化和清理:

首先,我们创建完一个类后,当我们需要对类进行初始化;而当我们完成工作后,需要对类进行清理。这些工作,分别需要调用构造函数析构函数

构造函数和析构函数的定义与特点:

构造函数:

1.作用:主要作用于对创建的对象成员属性进行初始化;
2.语法:类名+()+{}
3.注意事项:
构造函数没有返回值,但不需要写void;
构造函数函数名与类名一样;
构造函数可以有形参,可以发生重载;
构造函数只会调用一次,且会自动调用;

析构函数:

1.作用:主要作用于在对象销毁前对其进行清理;
2.语法:~类名+()+{}
3.注意事项:
析构函数没有返回值,但不需要写void;
析构函数函数名与类名一样;
析构函数没有形参,不可以发生重载;
析构函数只会调用一次,且会在程序销毁前自动调用;

编译器强制要求对对象进行初始化和清理,如果我们没有手动进行这些工作,那么编译器会自动帮我们提供构造函数和析构函数,只不过编译器提供的是空实现。

class Info
{
public:
	//构造函数
	Info()
	{
		cout<<"Y"<<endl;
	}
	//析构函数
	~Info()
	{
		cout<<"N"<<endl;
	}
};

int main()
{
	Info A;//只是初始化,并未调用任何成员变量
	return 0;
}

上面代码我们在主函数中没有调用这两个函数,但会自动调用:
在这里插入图片描述

初始化列表:

我们可以调用构造函数来进行成员属性初始化,这里还有一种初始化方法,就是利用初始化列表来对成员变量进行初始化。其实际上还是需要调用构造函数。

它的语法是:构造函数名+括号+冒号+属性赋值+大括号

    class Info
{
public:
    //构造函数,这里构造函数接收数据传给变量成员
	Info(int a,int b,string c) :id(a),tel(b),name(c)
	{
		
	}
private:
	int id;
	int tel;
	string name;
};
类对象作为类的成员:

有时候,我们往往需要在一个类中调用另一个类,类似于在一个函数中调用另一个函数。这时候一个类就作为另一个类的成员,这就会带来一些问题,是先调用哪一个类的构造函数,最后释放时,先调用哪个类的析构函数?

  1. 如果类A的成员属性包含类B,那么在使用类A时,首先调用类B的构造函数进行初始化,最后再调用类A的构造函数进行初始化;
  2. 如果类A的成员属性包含类B,那么在释放内存时,首先调用类A的析构函数,最后再调用类B的析构函数;

构造函数的分类:

构造函数可以有两种分类方法:
1.根据构造函数有无参数可以分为有参构造和无参构造;

	//无参构造函数
	Info()
	{
		cout << "调用无参构造函数" << endl;
	}
	//有参构造函数
	Info(int a)
	{
		cout << "调用有参构造函数" << endl;
	}

2.根据构造函数是否发生拷贝可以分为普通构造函数和拷贝构造函数;

	//无参构造函数
	Info()
	{
		cout << "调用无参构造函数" << endl;
	}
	//拷贝构造函数
	Info(const Info& p)
	{
		cout << "调用拷贝构造函数" << endl;
	}

拷贝构造函数,顾名思义,就是将传进来的类的属性拷贝到新的一个类中,令其在初始化时可以完成一套完整的复制工作;

系统提供的构造函数注意事项
当用户设定某些构造函数时,系统可能不提供某些构造函数

情况一:用户设置了默认构造函数:
系统还会提供的构造函数:

默认构造函数 ×
有参构造函数
拷贝构造函数

情况二:用户设置了有参构造函数:
系统还会提供的构造函数:

默认构造函数 ×
有参构造函数 ×
拷贝构造函数

情况三:用户设置了拷贝构造函数:
系统还会提供的构造函数:

默认构造函数 ×
有参构造函数 ×
拷贝构造函数 ×

构造函数的调用

构造函数有三种调用方法:小括号调用、显式调用、隐式转换调用;

1.小括号调用:

变量名后面不跟小括号视为调用默认构造函数(无参构造函数)、变量名后跟小括号视情况调用有参构造函数和拷贝构造函数;

	Info A;//调用默认构造函数
	Info B(2);//调用有参构造函数
	Info C(A);//调用拷贝构造函数

注意:若变量名后跟括号但没有参数,那么不会视为默认构造函数调用,将视为函数声明(数据类型为类Info,没有参数,函数名为A);

Info A();//函数声明

2.显式调用:

显式调用对于三种类型的构造函数分别对应三种调用方法:
1.默认构造函数调用:
与小括号调用类似;

	Info A;

2.有参构造函数调用:
设置一个匿名对象,将其作为等号右边,那么下列语句即可视为调用有参构造函数:

	Info B = Info(2);
	Info(2)//匿名对象

3.拷贝构造函数调用:
与有参构造函数类似,只不过传递参数不同:

	Info C = Info(B);

注意:不可以利用拷贝构造函数来初始化匿名对象(匿名对象一旦创建但未给予名称,随后会被立即销毁),因为编译器会将Info BInfo (B) 两个表达式等价:

	Info B(10);//有参构造函数调用
	Info(B);//拷贝构造函数初始化匿名对象

拷贝构造函数的调用情景:
1.当我们需要复制一个类的属性副本给另一个类;
2.接收实参类型为类的函数,会自动调用拷贝构造函数创造一份副本形参;
3.当函数返回类,接收的时候会调用拷贝构造函数;

class D
{
public:
	D()
	{
		cout<<"调用默认构造函数" << endl;
	}
	D(const D & p)
	{
		s = p.s;
		cout<<"调用拷贝构造函数" << endl;;
	}
	~D()
	{
		cout<<"调用析构函数" << endl;;
	}
private:
	int s;
};

D func(D var3)//形参,调用拷贝构造函数,类似于D var3=var
{
	D var4;//创建类,调用默认构造函数
	return var4;//返回参数
}

int main()
{
	D var;//创建类,调用默认构造函数
	D var2 = var;//复制一份副本,调用拷贝构造函数,类似于D var2=var
	D var5 = func(var);//接收参数副本,调用拷贝构造函数,类似于D var5=var4
	return 0;
}

3.隐式转换调用:

隐式转换与显式类似:

    Info A;//无参构造函数调用
	Info B = 10;//有参构造函数调用
	//类似于Info B = Info(10);
	Info C = B;//拷贝构造函数调用
	//类似于Info C = Info(B);

深拷贝和浅拷贝

浅拷贝和深拷贝都是对于拷贝构造函数而言的;如果我们的拷贝构造函数将接收的对象属性拷贝到栈区,那么就时浅拷贝;如果拷贝构造函数将接收到的对象属性拷贝到一个新开的堆区,那么就是深拷贝;

浅拷贝容易导致对之前堆区开辟的内存进行多次释放的问题

这里我们要将Test中的指针属性拷贝到Test2中,结束后调用析构函数中的delete关键字对开辟的堆区进行释放;但这里就有一个问题,因为我们使用的拷贝构造函数进行的是浅拷贝,即直接将一个堆区的地址多次复制粘贴给下一个对象;而最后我们进行销毁时,就会多次访问并释放这同一个空间,造成多次释放。

问题代码如下:


class Info
{
public:
	Info()
	{
		cout << "默认构造函数" << endl;
	}
	Info(int a)
	{
		id = a;
		point = new int(a);
		cout << "有参构造函数" << endl;

	}
	
	//有问题!!!
	//**********************************//
	Info(Info& p)
	{
	    id=p.id;
		point = p.point;
		cout << "拷贝构造函数" << endl;
	}
	//**********************************//
	
	~Info()
	{
		delete point;
		point = NULL;
		cout << "析构函数" << endl;
	}
private:
	int id;
	int* point;
};

int main()
{
	Info Test1(10);
	Info Test2(Test1);
	return 0;
}

于是这里我们选择利用深拷贝,即对指针指向的空间内容在堆区另外开辟一个空间存放,这样就不会对同一块内存进行多次释放,它的拷贝构造函数如下;

Info(Info& p)
	{
		id = p.id;
		point = new int(p.id);
		cout << "拷贝构造函数" << endl;
	}

静态成员:

静态成员变量:
在类中,用static关键字修饰的成员变量就会变成静态成员,它的空间在静态区分配,并且在编程时就已经分配好。静态成员有几个特点:

  1. 静态成员必须在类里面声明,在类外一定要初始化(在main函数初始化)
  2. 静态成员共用一块内存空间,无论对于哪个对象访问静态成员都只是访问同一块空间;
  3. 具体对象只能访问静态成员,所以我们不仅可以通过对象访问静态成员,还可以通过类访问静态成员:

class Info
{
public:
	static int id;
};
int Info::id = 312100;//初始化
int main()
{
	cout << Info::id << endl;
	return 0;
}
  1. 静态成员也有权限,如果我们给静态成员设置权限,那么类外也不可以访问;

class Info
{
private:
	static int id;
};
int Info::id = 312100;//初始化
int main()
{
	cout << Info::id << endl;//不可访问
	return 0;
}

静态成员函数:
类似地,我们也有静态成员函数,静态成员函数与静态成员变量类似,都具有共有性,只不过静态成员函数多一个注意事项:静态成员函数只能访问静态成员变量,不能访问其他非静态成员变量。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】对象初始化和清理 的相关文章

  • 使用 gcc 在 Linux 上运行线程构建块 (Intel TBB)

    我正在尝试为线程构建块构建一些测试 不幸的是 我无法配置 tbb 库 链接器找不到库 tbb 我尝试在 bin 目录中运行脚本 但这没有帮助 我什至尝试将库文件移动到 usr local lib 但这又失败了 任何的意见都将会有帮助 确定您
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • UVa 133 The Dole Queue(圈的下标处理)

    本题难点在于用数组处理圈状物时下标的计算 include
  • C++下几种新的数据类型

    一 CString 用法 CString s 123 对应库 include
  • 如何查看是否安装了java环境?

    一 win R 输入cmd 二 回车后进入cmd界面 三 在cmd窗口中输入java 若提示 java 不是内部或外部命令 也不是可运行的程序或批处理文件 则表示未安装JRE 这个错误提示表明系统无法找到 java 程序 可能是由于 Jav
  • Datatables

    1 10 x与1 9 x参数名对照表 Datatables 1 10 x在命名上与1 9 x的有区别 新版的使用的是驼峰的命名规则 而之前的是采用匈牙利命名规则 当然 这些变化都是向下兼容的 你可以继续使用旧版本的api方法的参数和名称 如
  • MongoDB安装配置详细教程

    1 安装平台 windows win7 win10均可 2 MongoDB安装包下载 MongoDB的安装包下载地址 https www mongodb com download center atlas 3 安装过程 1 运行下载的msi
  • 搞笑幽默段子-1(仅供娱乐)

    老师问小明 小明 你昨天为什么没来上课 小明回答 老师 我昨天去办事 结果碰到一只猪 我和它相视了一会儿 然后它居然把我当成兄弟 跟着我进了学校 我只好带着它一起上课 老师一脸错愕 你确定那只猪跟着你一起进了学校 小明犹豫片刻 回答 老师
  • node实现文件上传(1)

    1 FormData对象 以对象的方式来表示页面中的表单 又称为表单对象 以key value的方式来保存数据 XMLHttpRequest对象可以轻松的表单对象发送的服务器端 1 是一个构造函数 new FormData 2 常用的API
  • 分布式事务的二阶段提交和三阶段提交

    分布式事务 是指存在多个跨库事务的事务一致性问题 或者是指分布式架构下由多个应用节点组成的多个事务之间的事务一致性问题 在单点服务时 某电商会员购买上市时会出现以下情况 开启数据库事务 创建订单 会员积分减少 商品库存减少 提交数据库事务
  • MYSQL获取最近7天的日期

    写一个简单的 获取最近7填日期的sql select DATE SUB curdate INTERVAL 1 DAY day1 union select DATE SUB curdate INTERVAL 2 DAY day2 union
  • Zookeeper基础知识(一)

    一 Zookeeper是什么 ZooKeeper是一个开放源码的分布式协调服务 它是集群的管理者 监视着集群中各个节点的状态根据节点提交的反馈 进行下一步合理操作 最终 将简单易用的接口和性能高效 功能稳定的系统提供给用户 分布式应用程序可
  • echarts的grid——图表的位置配置

    博主 小猫娃来啦 文章核心 echarts的grid 图表的位置配置 文章目录 认识并了解grid grid有哪些属性 有什么用 关键点 grid所有属性 containLabel属性 总结 认识并了解grid 首先还是先认识grid 要弄
  • VMware中安装Ubuntu

    前言 本文档将演示通过 VMware 安装 Ubuntu 所使用的文件如下 VMware Workstation Pro ubuntu 16 04 6 desktop amd64 iso 本文档分为三个部分 新建虚拟机 安装系统 调整页面
  • gradle引入依赖包/maven引入依赖包

    1 打开https mvnrepository com 搜索出需要的jar包 2 点击进入需要jar包 选择maven或gradle的tab页 复制依赖url即可 如图 3 gradle项目的话 点击buildDependents即可 如图
  • 工业机器人仿真参考

    最近有一些朋友看到我做的关于Unity3d仿真机器人的项目 本次我在平台做以分享 希望的朋友或者有需要在此基础做开发的可以参考下 开发工具 下位机 Unity3D 上位机 Visual Studio 机械臂模型 TH6 QKM SCARA
  • Linux内核启动过程学习

    过完年来 敲着键盘都有点生疏了 很多东西都有点忘记了 还有刚上班还有点不习惯 状态还没有转换过来 没有办法趁这个机会就复习一下Linux的启动过程吧 asmlinkage void init start kernel void char c
  • 用python做自动化测试--序

    几年来 都有个想法 写个 用Python做自动化测试 的系列文章 把自己的经验和大家共享 和大家多交流 同时总结下自己掌握的东西 但苦于一直忙 其实准确点的说是自己懒 迟迟没有开始 我想不能再懒了 要不以后会更忙 更懒 我为什么选择pyth
  • Python统计学实例之正态分布:计算男女身高相差>5厘米的概率

    正态分布计算 示例1 正态分布计算实例 计算男女身高相差 gt 5厘米的概率 解题思路 用到的公式 总结 正态分布计算实例 计算男女身高相差 gt 5厘米的概率 假设男生身高 X N 71 20 25 女生身高 Y N 64 16 解题思路
  • Docker安装部署FastDFS详细过程

    FastDFS分布式文件存储的由来 在我们的项目中有很多需要存储的内容出现 比如图片 视频 文件等等 在早期的时候用户量不大 产生的文件也不是很多 这时我们可以把文件和服务程序放在一个服务器中 随着文件越来越多 服务器的资源会被文件资源大量
  • centos7:搭建部署confluence(6.10.0)

    1 test env host ip 20 20 20 20 host name centos7 ssh soft 2 安装jdk sudo rpm ivh jdk 8u181 linux x64 rpm sudo vim etc prof
  • 【C++】对象初始化和清理

    对象的初始化和清理 首先 我们创建完一个类后 当我们需要对类进行初始化 而当我们完成工作后 需要对类进行清理 这些工作 分别需要调用构造函数和析构函数 构造函数和析构函数的定义与特点 构造函数 1 作用 主要作用于对创建的对象成员属性进行初