c++——类和对象(1)构造,析构函数

类的六个默认函数

如果一个类当中没有成员的话,那叫空类,实际上空类有6个编译器默认生成的函数成员

默认成员函数:没有显示实现,编译器生成的成员函数称为默认成员函数

1,构造函数与构析函数

1.1构造函数的概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以

保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

1.2构造函数的特性

构造函数主要任务是初始化对象,并不是开辟空间

1. 函数名与类名相同。

2. 无返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

class Data {
public:
//无参构造函数
	Data(){
	_year = 1;
	_month = 1;
	_day = 1;
	}
//含参构造函数
	Data(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;


	}
//全缺省函数
	Data(int year = 2, int month = 2, int day = 2)
	{

	}
	
	void printf()
	{
		
		cout << _year << _month << _day << endl;//崩溃,
		cout << "printf()" << endl;//正常
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	
	Data d1;
	d1.printf();
	Data d2(2024, 2, 2);
	d2.printf();
	Data d3;
	d3.printf();
	return 0;
}


4.1无参构造函数与含参构造函数的使用,无参函数直接Data d1;不加括号为了

避免重复声明新函数

Data d1;
d1.printf();
Data d2(2024, 2, 2);
d2.printf();

4.2全缺省函数与无参构造函数,d1与d3构成重载函数,但下面会构成重载失误

Data d1;
d1.printf();

Data d3;
d3.printf();//d3与d1构成重载,

会出现下面错误

5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。

6. C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看 下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员 函数。

class Time {
public:
	Time()
	{
		cout << "time()" << endl;
		_hour = 0;
		_minute = 0;
	}
private:
	int _hour;
	int _minute;
};
class Data
{
public:

private:int _year;
	   int _month;
	   int _day;
	   Time _t;
};
int main()
{
	Data d;
	return 0;
}

上面的代码的处理结果是内置类型不一会被处理(具体看编译器vs不会处理),而自定义类型会被处理,Time是自定义类型所以会初始化

但只有是无参构造函数才会成功,若有参数会报错Time(int a){}

1.3析构函数的概念

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

1.4析构函数的特性

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 3)
 {
 _array = (DataType*)malloc(sizeof(DataType) * capacity);
 if (NULL == _array)
 {
 perror("malloc申请空间失败!!!");
 return;
 }
 _capacity = capacity;
 _size = 0;
 }
 void Push(DataType data)
 {
 // CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 // 其他方法...
 ~Stack()
 {
 if (_array)
 {
 free(_array);
 _array = NULL;
 _capacity = 0;
 _size = 0;

 }
 }
private:
 DataType* _array;
 int _capacity;
 int _size;
};
void TestStack()
{
 Stack s;
 s.Push(1);
 s.Push(2);
}

如果像这一类,申请了空间,没有调用显示析构函数,会有内存泄漏的风险,所以如果有类调用堆上的空间,一定要调用析构函数

2,拷贝构造函数

2,1概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用。

2.2特性

1, 拷贝构造函数的参数只有一个

2参数必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 // Date(const Date& d)   // 正确写法
    Date(const Date& d)   // 错误写法:编译报错,会引发无穷递归
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);
 return 0;
}

因为传值会调用拷贝构造函数,造成无限递归,所以用引用

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

4类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 10)
 {
 _array = (DataType*)malloc(capacity * sizeof(DataType));
 if (nullptr == _array)
 {
 perror("malloc申请空间失败");
 return;
 }

 _size = 0;
 _capacity = capacity;
 }
 void Push(const DataType& data)
 {
 // CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 ~Stack()
 {
 if (_array)
 {
 free(_array);
 _array = nullptr;
 _capacity = 0;
 _size = 0;
 }
 }
private:
 DataType *_array;
 size_t _size;
 size_t _capacity;
};
int main()
{
 Stack s1;
 s1.Push(1);
 s1.Push(2);
 s1.Push(3);
 s1.Push(4);
 Stack s2(s1);
 return 0;
}

这是为什么,是因为浅拷贝是在同一块空间,如果创建两个对象,s1,s2,都指向同一内存,调用析构函数时,会将同一块空间free两次,造成错误

5. 拷贝构造函数典型调用场景:

  1. 使用已存在对象创建新对象
  2. 函数参数类型为类类型对象
  3. 函数返回值类型为类类型对象
class Date {
public:
	Date(int year,int month,int day)
	{
		cout << "Data()"<<endl;
		_year = year;
		_month = month;
		_day = day;
	}
	~Date()
	{
		cout << "~Data()"<<endl;
	}
	Date(const Date& d)
	{
		cout << "Date(const Date&d)"<<endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
private:
	int _year;
	int _month;
	int _day;

};
Date test(Date d1)
{
	Date tem(d1);第二次拷贝构造
	return tem;//本来是第三次拷贝构造,但被编译被优化了因为第二次拷贝构造的值可以做返回值
}
int main()
{
	Date d1(2024, 2, 1);
	test(d1);//第一次拷贝构造函数


	
	return 0;
}

下面是结果

6.传值与引用的细分

1传值会多拷贝一次,比较麻烦,但是可以避免一些错误

2而引用虽然方便但是会有一些情况出问题;

 class Date {
	public:
		Date(int year,int month,int day)
		{
			cout << "Data()"<<endl;
			_year = year;
			_month = month;
			_day = day;
		}
		~Date()
		{
			cout << "~Data()"<<endl;
			_year = -1;
			_month = -1;
			_day = -1;
		}
		Date(const Date& d)
		{
			cout << "Date(const Date&d)"<<endl;
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		Date operator=(const Date& d)
				{
			_year = d._year;
			_month = d._month;
			_day =d._day;//this是d2的形参,d是_d4
			//然后d1是this的位置,d2是d4
					 return *this;
			}
		void printf()
		{
			cout << _year <<" " << _month <<" " << _day << endl;
		}
	private:
		int _year;
		int _month;
		int _day;
	
	};

 Date& fun()
 {
	 Date d(1, 2, 2);
	 return d;
 }void func()
 {

 }
 int main()
 {
	 Date d1(2024, 4, 14);
	 Date d2(d1);
	 Date d3 = d1;//拷贝构造,把存在的拷贝给不存在的
	 Date d4(2024, 5, 1);
	d1= d4;//赋值重载,不会调用拷贝构造函数

	 //连续赋值
	 d1 = d2 = d4;
	 //到这一行总共5个调用拷贝,但是把上面的变成引用返回,只有两个
return 0;

}

下面是结果

但是如果是引用的话

下面是一些错误的发生

 Date& fun()
 {
	 Date d(1, 2, 2);
	 return d;
 }
 int main()
 {
	 
	  Date& ret = fun();
	 ret.printf();
	 return 0;
 }

因为return d前先析构,再返回,本来是调试看到析构后this是随机值是野空间,如果下面在调用一个函数,就会可能会变野空间会  变化,随机值被改变
 

总 结  返回对象是临时对象,或局部变量不用引用返回,有风险
 1如果返回对象生命周期到了,不会析构,可以用传引用返回uobuyi

创作不易,希望点赞!!!

目录

类的六个默认函数

1,构造函数与构析函数

2,拷贝构造函数


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/561818.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c语言之字符串的集合存放形式

采用指针分配的二维数组与直接定义的二维数组&#xff0c;sizeof的不同 采用指针分配的二维数组&#xff1a; 它的遍历方式是&#xff1a; 上面这个是分配二级指针的地址&#xff0c;二级指针就是一片可以用来分配一级指针空间的地址&#xff0c;然后指针寻址本来就可以当成数组…

java算法day3

移除链表元素设计链表翻转链表两两交换链表中的结点 移除链表元素 ps&#xff1a;有时候感觉到底要不要写特判&#xff0c;你想到了就写&#xff01;因为一般特判有一劳永逸的作用。 解法有两种&#xff0c;一种是不用虚拟头结点&#xff0c;另一种就是用虚拟头结点。 这里我…

python安装的详细步骤

下载 1.打开Python官网.我们建议工具类的测试软件还是官网下载比较靠谱. https://www.python.org/getit/ 2.在下图界面中选择需要的方式进行点击 3.直接点击下载.可以进入保存界面,进行保存即可下载,后续安装 4.鼠标放在Downloads显示平台和版本选择界面,点击Windows,进入wi…

Spring-dataSource事务案例分析-使用事务嵌套时,一个我们容易忽略的地方

场景如下&#xff1a; A_Bean 中的方法a()中调用B_Bean的b();方法都开启了事务&#xff0c;使用的默认的事务传递机制&#xff08;即&#xff1a;属于同一事务&#xff09;&#xff1b; 如下两种场景会存在较大的差异&#xff1a; 在b()方法中出现了异常&#xff0c;在b()中进…

Unity射击游戏开发教程:(2)实例化和销毁游戏对象

现在我们有了“飞船”,我们可以在屏幕上移动它,现在我们需要发射一些激光!与宇宙飞船一样,我们将让事情变得简单并使用 Unity 自己的基本形状。舱体的效果很好,所以我们来创建一个。 我们保存了有关位置、旋转和缩放的信息。我们想要缩小这个对象,假设每个轴上缩小到 0.2…

【树莓派学习】hello,world!

系统安装及环境配置详见【树莓派学习】系统烧录及VNC连接、文件传输-CSDN博客 树莓派内置python3&#xff0c;可以直接利用python输出。

Jetson nx 外接OLED屏幕

40 针 GPIO 引脚 GPIO引脚可以用作输入或输出端口&#xff0c;它们提供了一个数字电平以使用户在外界设备上进行控制或读取。Jetson TX2 NX共有198个GPIO引脚&#xff0c;分为三个不同的管脚组&#xff1a;J1、J21和J22。每个管脚组都具有数字输入/输出和PWM功能。 以下是 TX2…

语音转换中的扩散模型——DDDM-VC

DDDM-VC: Decoupled Denoising Diffusion Models with Disentangled Representation and Prior Mixup for Verifed Robust Voice Conversion https://ojs.aaai.org/index.php/AAAI/article/view/29740https://ojs.aaai.org/index.php/AAAI/article/view/29740 1.概述 首先,语…

谷歌Gemini 1.5 Pro国内怎么用?国内镜像来了

长期以来&#xff0c;许多人向我咨询是否存在一个稳定而高效的全球AI大模型测试平台&#xff0c;这个平台需要不仅真实可靠&#xff0c;而且能够提供稳定和快速的服务&#xff0c;不会频繁出现故障或响应缓慢的问题。然而&#xff0c;当我发现了AskManyAI时&#xff0c;我被其所…

ModuleNotFoundError: No module named ‘scripts.animatediff_mm‘ 解决方案

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 大家好,我是水滴~~ 本文主要介绍在使用 Stable Diffusion WebUI 安装 AnimateDiff 插件后出现的ModuleNotFoundError: No module named scripts.animatediff_mm异常的解决方案,希望…

初识ansible变量及实例配置

目录 1、为什么要使用变量 2、变量分类 3、 变量详解 3.1 vars,vars_files , group_vars 3.1 .1 vars 剧本中定义变量 3.1.2 vars_file 将变量存放到一个文件中&#xff0c;并在剧本中引用 3.1.3 group_vars 创建一个变量文件给某个组使用 实例1-根据不同的主机…

力扣HOT100 - 19. 删除链表的倒数第N个节点

解题思路&#xff1a; 链表题目&#xff1a;哑节点、栈、快慢指针&#xff08;双指针&#xff09; 方法一&#xff1a;计算链表长度 class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dum new ListNode(0, head);int len getLen(head);…

linux内核初始化成功后是如何过渡到android初始化的

Android用的linux内核&#xff0c;以完成OS该有的功能&#xff0c;例如&#xff0c;文件系统&#xff0c;网络&#xff0c;内存管理&#xff0c;进程调度&#xff0c;驱动等 &#xff0c;向下管理硬件资源向上提供系统调用。另一些Android特有驱动也放在内核之中。 当linux内核…

Java关键字和API

1 this和super关键字 1.this和super的意义 this&#xff1a;当前对象 在构造器和非静态代码块中&#xff0c;表示正在new的对象 在实例方法中&#xff0c;表示调用当前方法的对象 super&#xff1a;引用父类声明的成员 无论是this和super都是和对象有关的。 2.this和sup…

详解数据结构之-「数组篇」

详解数据结构之-「数组篇」 太久没有写算法了&#xff0c;最近在回顾数据结构和算法&#xff0c;准备把这块重新捡起来&#xff0c;给自己立一个flag&#xff0c;每周花时间练习算法&#xff0c;写一篇总结。 数组 数组数据结构&#xff08;英语&#xff1a;array data stru…

Mysql学习2

目录 一.数据库&#xff1a; 1.创建数据库&#xff1a; 2.查看数据库&#xff1a; 3.备份恢复数据库&#xff1a; 二.表 1.创建表指令&#xff1a; 2.MySQL常用数据类型&#xff1a; 3.删除与修改表&#xff08;重点&#xff09;&#xff1a; 4.数据库CRUD语句&#xf…

网络 (TCP/IP 四层协议中常见网络协议)

应用层 DNS (Domain Name System) 域名系统. DNS 是一整套从域名映射到 IP的系统 NAT 技术 解决 IP 地址不够用的问题. 可以实现私有 IP 和全局 IP 的相互转换 NAPT 技术 使用 IP Port 唯一确定局域网中的主机 传输层 TCP 协议 (Transmission Control Protocol 传输控制协议…

C++:范围-based for 循环

范围-based for 循环是 C11 引入的一种循环语法&#xff0c;它简化了遍历容器和数组等序列的操作&#xff0c;使代码更加清晰和简洁。它通常用于遍历容器类&#xff08;如数组、向量、列表等&#xff09;中的元素&#xff0c;或者以范围的形式遍历初始化列表。 范围-based for …

AI大模型探索之路-实战篇1:基于OpenAI智能翻译助手实战落地

文章目录 前言一、需求规格描述二、系统架构设计三、技术实施方案四、核心功能说明五、开源技术选型六、代码实现细节1.图形用户界面&#xff08;GUI&#xff09;的开发2.大型模型调用的模块化封装3.文档解析翻译结果处理 总结 前言 在全球化的浪潮中&#xff0c;语言翻译需求…

HarmonyOS NEXT 使用Canvas实现模拟时钟案例

介绍 本示例介绍利用 Canvas 和定时器实现模拟时钟场景&#xff0c;该案例多用于用户需要显示自定义模拟时钟的场景。 效果图预览 使用说明 无需任何操作&#xff0c;进入本案例页面后&#xff0c;所见即模拟时钟的展示。 使用说明 无需任何操作&#xff0c;进入本案例页…