【03】C++ 类和对象 2:默认成员函数

文章目录

  • 🌈 前言
  • 🌈 Ⅰ 构造函数
    • 1. 构造函数概念
    • 2. 构造函数特性
    • 3. 初始化列表
  • 🌈 Ⅱ 析构函数
    • 1. 析构函数概念
    • 2. 析构函数特性
  • 🌈 Ⅲ 拷贝构造
    • 1. 拷贝构造概念
    • 2. 拷贝构造特性
    • 3. 深度拷贝构造
  • 🌈 Ⅳ 赋值重载
    • 1. 运算符重载
    • 2. 赋值运算符重载

🌈 前言

1. 默认成员函数介绍

  • 一个什么成员都没有的类简称为空类,编译器会自动为空类生成几个默认成员函数。
  • 默认成员函数:用户不写出来的话,编译器就会生成的成员函数称为默认成员函数。
  • 编译器自动生成的默认成员函数一般都比较挫,因此在大多数情况下就需要我们自己去编写这些个默认成员函数的执行逻辑。

2. 默认成员函数分类

函数功能
构造函数主要完成对成员变量的初始化工作
析构函数主要完成对成员变量的清理工作
拷贝构造使用同类对象初始化新创建的对象
赋值重载把一个对象赋值给另一个对象

🌈 Ⅰ 构造函数

1. 构造函数概念

  • 现定义一个日期 (date) 类
class date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	date d1, d2;
	d1.Init(2024, 2, 6);	// 调用公有成员函数 Init 为对象 d1 内的成员变量初始化
	d2.Init(2024, 2, 7);	// 调用公有成员函数 Init 为对象 d2 内的成员变量初始化

	return 0;
}
  • 对于 date 类,可以使用 Init 公有成员函数来给对象设置日期,但如果每次创建对象时都需要调用该函数来进行成员变量的初始化,需要写两行且很容易忘记,此时构造函数就诞生了。
  • 构造函数是一个特殊的成员函数,该成员函数的名字与类名一致,实例化对象时由编译器自动调用。用来保证每个对象内的成员变量都有一个适当的初始值,且在对象整个生命周期内只调用一次

2. 构造函数特性

  • 构造函数的主要任务是初始化对象

1. 构造函数特性

  1. 函数名和类型相同。
  2. 没有任何返回值。
  3. 实例化对象时编译器自动调用对应的构造函数。
  4. 构造函数也支持重载。
  5. 构造函数也支持缺省参数。

2. 构造函数示例

class date
{
public:
	// 构造函数:函数名和类名相同,没有返回值,支持缺省参数
	date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	date d1;				// 不指定初始值时就用缺省参数初始化对象
	date d2(2024, 2, 7);	// 在实例化对象的同时顺带就能初始化对象

	return 0;
}

在这里插入图片描述

  • d1 使用缺省参数完成了对象初始化,d2 使用给的值完成了对象初始化。都自动调用了构造函数。

3. 初始化列表

1. 为何使用初始化列表

  • 在构造函数的函数体内对成员变量初始化称为初始化赋值,并不是正儿八经的初始化,是赋值就存在多次赋值的问题。
  • 初始化赋值的问题在构造函数的函数体内没办法解决。因此在构造函数时可以使用一种叫做初始化列表的方式进行初始化。用以确保每个成员变量都只被初始化一次。
class date
{
public:
	date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
		_year = 2023;	// 成员变量 _year 被初始化了 2 次这咋个整
	}
private:
	int _year;
	int _month;
	int _day;
};

2. 初始化列表语法格式

类名(形参列表)
	:成员变量1(成员变量 1 的初始值)
	,成员变量2(成员变量 2 的初始值)
	,成员变量n(成员变量 n 的初始值)
{}

3. 初始化列表示例

class date
{
public:
	date(int year = 1, int month = 1, int day = 1)
		:_arr((int*)malloc(4 * sizeof(int)))
		,_year(year)
		,_month(month)
		,_day(day)
	{
		cout << "这是一个构造函数" << endl;
	}
private:
	int* _arr;
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述

4. 初始化列表的特性

  1. 每个成员变量在初始化列表中只能出现一次 (只能初始化一次)。
  2. 以下成员变量必须放在初始化列表中进行初始化 (在函数体内对这些成员变量初始化会报错)。
    • 引用成员变量
    • const 成员变量
    • 自定义类型成员 (且该类没有默认构造函数时)
  3. 尽量使用初始化列表进行初始化,因为编译器会优先使用初始化列表。
  4. 成员变量的声明顺序就是成员变量在初始化列表中初始化的顺序。

在这里插入图片描述

🌈 Ⅱ 析构函数

1. 析构函数概念

1. 概念

  • 构造函数将对象内的成员变量初始化,那么析构函数就是将其销毁。
  • 析构函数不是完成对对象本身的销毁,对象是在出了对象所在的作用域或者程序结束时自动销毁。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2. 格式

~类名()
{	
	// 函数体
}

2. 析构函数特性

1. 析构函数特性

  1. 析构函数的函数名由 ~ 和 类名 两部分构成。
  2. 析构函数没有任何参数,也没有返回值。
  3. 一个类中只能由一个析构函数。
  4. 析构函数不能实现函数重载。
  5. 对象的声明周期结束时,编译器会自动调用析构函数。
  6. 如果类初始化时没有申请资源 (开辟空间),析构函数可以不写,反之一定要写。

2. 析构函数示例

  • 现有一个为了实现栈而定义的类。
class stack					// 定义一个用于实现栈的类
{
public:
	stack(int capacity = 4)	// 构造函数
		:_array((int*)malloc(4 * sizeof(int)))
		, _top(-1)
		,_capacity(capacity)
	{
		cout << "stack(int capacity = 4)" << endl;
	}

	~stack()				// 析构函数:对象的生命周期结束时自动调用析构函数
	{
		free(_array);		// 如果有动态开辟的空间,就不用怕最后会忘记释放了
		_top = 0;
		_capacity = 0;
		cout << "~stack()" << endl;
	}
private:
	int* _array;
	int _top;
	int _capacity;
};
  • 使用上述 stack 类定义出对象的话就肯定要动态开辟空间,如果没有析构函数自动将开辟的空间释放掉,而自己又忘了将开辟的空间手动释放,内存泄漏这不就来了。

在这里插入图片描述

🌈 Ⅲ 拷贝构造

1. 拷贝构造概念

1. 拷贝构造概念

  • 在实例化对象时,可以不给初始值让构造函数使用缺省参数,也可以给初始值让构造函数对对象进行初始化。拷贝构造就是用一个现有的同类对象去初始化另一个对象

  • 拷贝构造函数只有一个形参 (只显示一个形参,this 指针不显示),该形参是对本类类型对象的引用 (一般常用 const 修饰),在用已存在的同类对象创建新对象时自动调用

2. 拷贝构造语法格式

类名(const 类名& 形参名)	// 实际上还是有两个形参,第一个形参为隐藏的 this 指针
{
	// 拷贝构造的函数体
}

3. 拷贝构造函数示例

class date
{
public:
	// 构造函数
	date(int year = 1, int month = 1, int day = 1)
		: _day(day)	
		,_year(year)
		,_month(month)
	{}

	// 拷贝构造函数
	date(const date& d)
		:_year(d._year)
		,_month(d._month)
		,_day(d._day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	date d1(2024, 2, 8);	// 调用构造函数对 d1 进行初始化
	date d2(d1);			// 调用拷贝构造使用 d1 对 d2 进行初始化

	return 0;
}

在这里插入图片描述

2. 拷贝构造特性

  1. 拷贝构造函数是构造函数的一个函数重载形式,本质还是构造函数。
  2. 拷贝构造函数的显示参数只有一个必须是对实参对象的引用
  3. 如果自己不写拷贝构造函数,编译器会自动生成,默认的拷贝构造函数执行的是浅拷贝。
  4. 拷贝构造函数最常用的调用场景:
    • 使用现有对象初始化创建新对象。
    • 函数参数类型为类类型对象。
    • 函数返回值为类类型对象。

3. 深度拷贝构造

1. 默认的拷贝构造函数执行的是浅拷贝

  • 浅拷贝:如果某个对象内的一个成员变量是一个指向一块连续空间的指针,那么浅拷贝就是将该地址拷贝给另一个对象的。两个对象各自的成员变量指向同一块空间。
  • 深拷贝:为新对象重新开辟一块同样大小的空间,并且将已有对象内的值拷贝过去。

在这里插入图片描述
在这里插入图片描述

2. 深度拷贝构造示例

class stack
{
public:
	stack(int capacity = 4)	// 构造函数
	{
		_array = (int*)malloc(sizeof(int) * capacity);
		assert(_array);
		_top = -1;
		_capacity = capacity;
	}

	stack(const stack& s)	// 拷贝构造,this 是 st2,s 是 st1
	{
		int* tmp = (int*)malloc(s._capacity * sizeof(int));
		assert(tmp);

		// 将 st1 的 array 中的有效数据拷贝给 st2 的 array
		memcpy(tmp, s._array, sizeof(int) * (s._top + 1));

		_array = tmp;
		_top = s._top;
		_capacity = s._capacity;
	}
private:
	int* _array;
	int _top;
	int _capacity;
};

在这里插入图片描述

🌈 Ⅳ 赋值重载

1. 运算符重载

1. 运算符重载概述

  • 有些时候函数名无法一眼看出该函数是为了实现什么功能 (如 func1、func2 这种函数名完全看不出该函数是要用来干什么)。
  • 运算符重载是具有特殊函数名的函数,是为了增强代码的可读性而被引入。

2. 运算符重载格式

函数返回值类型 operator操作符(形参列表)
{
	函数体
}

3. 赋值运算符示例

  • 现在要判断两个日期类的对象是否相等,内置操作符无法直接进行判断。因此将 == 进行重载以表示这是一个判断对象是否相等的成员函数。
class date
{
public:
	date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 将 == 重载成判断两个类类型对象是否相等的运算符
	bool operator== (const date& d)	// 此处的 this 表示 d1,d 表示 d2
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述

4. 运算符重载本质

  • 上述的 d1 == d2 实际上在编译器看来是 d1.operator==(d2),本质上还是调用对应的成员函数,然后将 d1 的地址传给 this 指针,形参 d 引用了 d2。

5. 运算符重载特性

  1. 不能通过连接其他符号来创建新的操作符,如 operator 和 @ 组成的 operator@ 不是一个新的操作符。
  2. 重载操作符必须有一个自定义类型的参数。
  3. 不能改变用于内置类型的运算符的含义,如 不能将 加法 的含义重载成 减法。
  4. 除了以下 5 种运算符,其余运算符都能被重载:
    • 点星 ( .* ),域作用限定符 ( :: ),计算大小 ( sizeof ),三目运算符 ( ?: ),点 ( . )

2. 赋值运算符重载

1. 赋值重载功能

  • 实现类类型对象之间的赋值,现在有两个已经被实例化好的对象 A、B,赋值重载就是将 对象 A 的值赋值给 对象 B。
  • 和拷贝构造不一样,拷贝构造是用一个定义好的对象去初始化一个未被定义的对象。

2. 赋值重载格式

  • 形参类型:const 类名&,传引用可以提高传参效率。
  • 返回值类型:类名&,设置返回值是为了支持连续赋值 (A = B = C 这样)。
  • 检测是否自己给自己赋值
  • 返回 *this,为了实现连续赋值。

3. 赋值重载示例

class date
{
public:
	date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	date& operator=(const date& d)	// this 指针指向 d1,d 表示 d2
	{
		if (this != &d)				// 避免自己给自己赋值
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;				// 返回对 d1 的引用
	}
private:
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述

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

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

相关文章

Selenium自动化测试框架的搭建

说起自动化测试&#xff0c;我想大家都会有个疑问&#xff0c;要不要做自动化测试&#xff1f; 自动化测试给我们带来的收益是否会超出在建设时所投入的成本&#xff0c;这个嘛别说是我&#xff0c;即便是高手也很难回答&#xff0c;自动化测试的初衷是美好的&#xff0c;而测试…

【Linux】vim的基本操作与配置(下)

Hello everybody!今天我们继续讲解vim的操作与配置&#xff0c;希望大家在看过这篇文章与上篇文章后都能够轻松上手vim! 1.补充 在上一篇文章中我们说过了&#xff0c;在底行模式下set nu可以显示行号。今天补充一条&#xff1a;set nonu可以取消行号。这两条命令大家看看就可…

SpringCloud-Ribbon:负载均衡(基于客户端)

6. Ribbon&#xff1a;负载均衡(基于客户端) 6.1 负载均衡以及Ribbon Ribbon是什么&#xff1f; Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。简单的说&#xff0c;Ribbon 是 Netflix 发布的开源项目&#xff0c;主要功能是提供客户端的软件负…

JRebel激活-nginx版本

nginx转发流量&#xff08;代替其他网上说的那个工具&#xff09; proxy_pass http://idea.lanyus.com; 工具激活 填写内容说明&#xff1a; 第一行的激活网址是&#xff1a;http://127.0.0.1:8888/ 正确的GUID。GUID 可以通过专门的网站来生成&#xff08;点击打开&#…

Redis事务和Redis管道

文章目录 1.Redis事务1.1 Redis事务是什么&#xff0c;能干嘛&#xff1f;1.2 Redis事务和数据库事务的差异1.3 Redis事务的相关命令 2.Redis管道2.1 Redis管道是什么2.2 管道与原生批量命令对比2.3 管道与事务对比2.4 使用管道注意事项 1.Redis事务 1.1 Redis事务是什么&…

机器学习:分类决策树(Python)

一、各种熵的计算 entropy_utils.py import numpy as np # 数值计算 import math # 标量数据的计算class EntropyUtils:"""决策树中各种熵的计算&#xff0c;包括信息熵、信息增益、信息增益率、基尼指数。统一要求&#xff1a;按照信息增益最大、信息增益率…

c++随机数生成进阶random与随之种子生成器的使用

随机数的作用我就不说了&#xff0c;但凡要用随机数的童鞋一定是有这个需求。下面我们就分三个层次来介绍随机数生成。 文章目录 一、利用rand函数生成随机数1、rand裸奔2、随机数种子srand-随机数生成器3、如何得到不同的种子值&#xff08;1&#xff09;、利用系统时间戳tim…

文件上传-Webshell

Webshell简介 webshell就是以aspphpjsp或者cgi等网页文件形式存在的一种命令执行环境&#xff0c;也可以将其称做为一种网页木马后门。 攻击者可通过这种网页后门获得网站服务器操作权限&#xff0c;控制网站服务器以进行上传下载文件、查看数据库、执行命令等… 什么是木马 …

2024.02.08

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);this->setWindowIcon(QIcon(":/zh.png"));ui->lineEdit->setPlaceholderText("账号/手…

【Linux笔记】缓冲区的概念到标准库的模拟实现

一、缓冲区 “缓冲区”这个概念相信大家或多或少都听说过&#xff0c;大家其实在C语言阶段就已经接触到“缓冲区”这个东西&#xff0c;但是相信大家在C语言阶段并没有真正弄懂缓冲区到底是个什么东西&#xff0c;也相信大家在C语言阶段也因为缓冲区的问题写出过各种bug。 其…

再识C语言 DAY16【进制的转换 】

文章目录 前言进制的转换一、各个进制的组成二、二进制转换其他进制三。其他进制转换为二进制四.小数部分进制转换五.八进制与十进制的相互转换 总如果您发现文章有错误请与我留言&#xff0c;感谢 前言 本文章总结于此视频 进制的转换 一、各个进制的组成 1. 二进制&#x…

【C语言自定义类型详解进阶】结构体(补充结构体的对齐和位段,一口气看完系列,央妈都点赞的博文)

目录 1.结构体 1.1 结构的基础知识 1.2 结构的声明 1.2.1特殊的声明&#xff08;匿名结构体类型&#xff09; 1.3结构体变量的定义 1.4关于匿名结构体类型的补充 1.5结构体的自引用 1.6结构体变量的初始化 2.结构体内存对齐&#xff08;重点&#xff09; 2.1偏移量补…

报错ValueError: Unknown CUDA arch (8.6) or GPU not supported

文章目录 问题描述解决方案参考文献 问题描述 报错 ValueError: Unknown CUDA arch (8.6) or GPU not supported 本人显卡为 RTX 3060&#xff0c;CUDA 为 10.2&#xff0c;PyTorch 为 1.5 解决方案 修改 C:\Users\Administrator\Envs\test\Lib\site-packages\torch\utils\c…

nvm安装nodejs 报错certificate has expired or is not yet valid

今天在使用nvm安装nodejs时&#xff0c;突然报如下错误&#xff1a; 从报错信息中很容易知道这是因为镜像凭证过期&#xff0c;所以我们只需要换个镜像即可。 打开你nvm的安装目录下的settings.txt文件&#xff0c;将下面两行添加到里面&#xff0c;如果已经有的就覆盖。 nod…

【制作100个unity游戏之24】unity制作一个3D动物AI生态系统游戏3(附项目源码)

最终效果 文章目录 最终效果系列目录前言随着地面法线旋转在地形上随机生成动物不同部位颜色不同最终效果源码完结系列目录 前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第24篇中,我们将探索如何用unity制作一…

一周学会Django5 Python Web开发-Django5创建项目(用命令方式)

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计11条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

前端滚动组件分享

分享一个前端可视化常用的卡片列表滚动组件&#xff0c;常用于可视化项目左右两侧的卡片列表的滚动。效果如下图所示&#xff1a; 组件描述 当鼠标移入滚动区域时&#xff0c;滚动行为停止当鼠标再次离开时&#xff0c;滚动继续 源码展示 <template><div ref"…

停车场|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)

停车场管理系统目录 目录 基于Springboot的停车场管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能实现 &#xff08;1&#xff09;车位管理 &#xff08;2&#xff09;车位预订管理 &#xff08;3&#xff09;公告管理 &#xff08;4&#…

Zoho Mail企业邮箱商业扩展第2部分:企业运营

在关于Zoho Mail企业邮箱商业扩展应用的这个系列的第一部分中&#xff0c;我们遇到了一位名叫王雪琳的个体企业家。她经营着自己的营销机构&#xff0c;并展示了如何创建和管理自己的企业。为了提升企业的专业形象&#xff0c;王雪琳使用了Zoho Mail来建立个性化的电子邮件域名…

【Linux】线程

线程 我们常常会在linux中或者在操作系统这门课中听到进程和线程的名称&#xff0c;我们之前认识了进程的概念&#xff0c;现在我们来了解一下线程的概念 线程概念&#xff1a; 什么是线程 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定…
最新文章