【C++类和对象】类有哪些默认成员函数呢?(上)

news2025/3/1 11:26:29

目录

1. 类的6个默认成员函数

2. 构造函数(*^▽^*)

2.1 概念

2.2 特性

3. 析构函数(*^▽^*)

3.1 概念

3.2 特性

4. 拷贝构造函数(*^▽^*)

4.1 概念

4.2 特性

5. 赋值运算符重载(*^▽^*)

5.1 运算符重载

5.2 赋值运算符重载


ヾ(๑╹◡╹)ノ"人总要为过去的懒惰而付出代价!ヾ(๑╹◡╹)ノ"


1. 类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

2. 构造函数(*^▽^*)

2.1 概念

对于data类:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2022, 7, 5);//初始化
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);//初始化
	d2.Print();
	return 0;
}

对于上面的这个代码,可以通过 Init 公有方法给对象设置日期,每次创建对象时都调用该方法设置信息。

构造函数 是一个 特殊的成员函数, 名字与类名相同 , 创建类类型对象时由编译器自动调用 ,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2 特性

构造函数 是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务 并不是开 空间创建对象,而是初始化对象
特征:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器 自动调用 对应的构造函数。
4. 构造函数可以重载。【但是我们大部分写的是全缺省的构造函数,一般不用函数重载【写一个默认的构造函数+别的函数重载】】
class Date
{
public:
	// 1.无参构造函数
	Date()//构造函数:函数名与类名相同,无返回值
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
	Date d1; // 调用无参构造函数,注意,不能写成Date d1();
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
}
5. 如果类中没有显式定义构造函数,则 C++ 编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。【即没有实现构造函数,C++编译器就会自动生成一个无参的默认构造函数】

 默认构造函数:(1)无参的构造函数(2)全缺省的构造函数(3)C++编译器生成的无参的构造函数【即三种必须要有一种,如果没有默认的构造函数【写的构造函数不是无参的,也不是全缺省的】就会报错】

6. 关于编译器生成的默认成员函数,在不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用? 对象调用了编译器生成的默认构造函数,但是 对象 _year/_month/_day,依旧是随机值【内置类型仍然是随机值】。也就说在这里 编译器生成的默认构造函数并没有什么用?
答: C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用 class/struct/union 等自己定义的类型。当不实现构造函数的情况下,编译器生成默认的构造函数会对自定类型成员 调用的它的默认成员函数。

如果一个类中的成员全是自定义类型,我们就可以用默认生成的函数。如果有内置类型函数成员,或者需要显示传参初始化,那么都要自己实现构造函数。(需要传参初始化,需要自己实现构造函数)【大部分都是自己写构造函数】

C++编译器默认生成构造函数内置类型函数成员变量不作处理,自定义类型成员会去调用它自己的默认构造函数。

注意: C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即: 内置类型成员变量在类中声明时 可以给默认值 。【注意:这里是声明,所以是一个缺省值,并不是初始化】
7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
class Date
{
public:
	Date()//无参的构造函数
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)//全缺省的构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void Test()
{
	Date d1;//这里会发生错误,默认构造函数只能有一个
}

运行时都会发生错误,出现冲突。

一般情况一个C++类,都要自己写构造函数。只有极少数情况下可以让编译器自动生成:

(1)类里面成员都是自定义类型成员, 并且这些成员都提供了默认构造函数。

(2)内置类型成员声明时给出缺省值。

3. 析构函数(*^▽^*)

3.1 概念

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

3.2 特性

析构函数 是特殊的成员函数。
特征:
1. 析构函数名是在类名前加上字符 ~
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数【自定义类型的变量会去调用它自己的析构函数处理,内置类型的变量不作处理】。 注意:析构函数不能重
4. 对象生命周期结束时,C++ 编译系统系统自动调用析构函数。
5.  关于编译器自动生成的析构函数,编译器生成的默认 析构函数,对会自定义类型成员调用它自己的析构函数。
6 . 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date 类;有 资源申请时,一定要写,否则会造成资源泄漏,比如 Stack 类【总之,有资源申请时,析构函数必须写】。

4. 拷贝构造函数(*^▽^*)

4.1 概念

 创建对象时,创建一个与一个对象一某一样的新对象。

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

4.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)//拷贝构造函数
	{
		_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;
}

传值方式:调用拷贝构造会调用拷贝构造函数,调用这个函数需要先传参,传参是一份临时拷贝,此时的状态是临时拷贝对象,相当于又是拷贝构造,又需要调用拷贝构造函数,此时就会引发无穷递归调用。

引用传参:就不会调用拷贝构造。

【一个对象初始化这个类的另一个对象,就是拷贝构造】【进行拷贝对象就是拷贝构造】

自定义类型对象,拷贝初始化要调用拷贝构造完成。

若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数 对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。【即内置类型的成员会完成值拷贝(浅拷贝),自定义类型的成员,去调用这个成员的拷贝构造】
结论:一般的类,自己生成的拷贝构造就够用了,不需要自己写拷贝构造。但是像stack这样的类,直接自己管理资源,就需要自己实现深拷贝。
4. 那么编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了 ,我们还需要自己实现吗?当然像日期类这样的类是没必要的。栈这样的类是不可以的。【栈里边有一个地址,那么两个栈就会执向同一块空间,增删查改的时候会互相影响以及析构的时候同一块空间会被析构两次,同一块空间释放两次,程序会崩溃 ——结局方案,自己实现拷贝构造-深拷贝】
类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

 为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

5. 赋值运算符重载(*^▽^*)

5.1 运算符重载

C++ 为了 增强代码的可读性 引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为 :关键字 operator 后面接需要重载的运算符符号
函数原型 返回值类型  operator 操作符 ( 参数列表 )
返回值:运算法运算后结果
参数:运算法操作数
注意:
(1)不能通过连接其他符号来创建新的操作符:比如 operator@
(2)重载操作符必须有一个类类型参数
(3)用于内置类型的运算符,其含义不能改变,例如:内置的整型 + ,不 能改变其含义
(4)作为类成员函数重载时,其形参看起来比操作数数目少 1 ,因为成员函数的第一个参数为隐藏的this
(5).*    ::    sizeof    ?:     . 注意以上 5 个运算符不能重载。这个经常在笔试中出现。
//日期的判断是否相等
bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year 
		&& d1._month == d2._month 
		&& d1._day == d2._day;
 }
//放到类里面 Date,此时是在类外面的写法
//1.
if (operator==(d1, d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成1.

(1)内置类型是可以直接用各种运算符,但是自定义类型是不可以直接用各种运算法的。为了让自定义类型可以使用各种运算符,所以就有了运算符重载。

(2)有多少个操作数就有多少个函数参数。(==两个操作数;++一个操作数;)

在类里面的写法

//日期的判断是否相等
bool operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
//放到类里面 Date,此时是在类里面的写法
//1.
if (d1.operator==(d2))
{
	cout << "==" << endl;
}
//2/
if (d1 == d2)
{
	cout << "==" << endl;
}
//1.和2.是等价的,编译器会处理成对应重载运算法调用

如果两个代码都存在(编译器是可以通过的,因为符合函数重载),编译器优先使用类里面的运算符重载。

//判断日期小
bool operator<(const Date& d)
{
	//小的情况
	if (_year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}

这里注意细节,不能想当然。

5.2 赋值运算符重载

1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回 *this
5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝
1. 赋值运算符重载格式
参数类型 const T& ,传递引用可以提高传参效率
返回值类型 T& ,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回 *this :要复合连续赋值的含义
//d2 = d1;-> d2.operator(&d2, d1)
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}
int main()
{

	Date d1(2000, 8, 20);
	Date d2(2000, 9, 17);
	Date d3(d1);//拷贝构造,一个存在的对象去初始化另一个要创建的对象
	d2 = d1;//赋值重载(复制拷贝) 两个已经存在的对象之间赋值
}
2.赋值运算符只能重载成类的成员函数不能重载成全局函数
赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数,但是
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的
赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数
3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝 。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
4.编译器生成的默认赋值重载函数已经可以完成字节序的值拷贝了 ,我们还需要自己实现吗?当然像日期类这样的类是没必要的。如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

补充知识:空指针是不存在的吗?是存在的,空地址是一个存在的地址。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp 左右滑动切换页面并切换tab

实现效果如图 要实现底部内部的左右滑动切换带动上方tab栏的切换&#xff0c;并且下方内容要实现纵向滚动 &#xff0c;所以需要swiper&#xff0c;swiper-item,scroll-view组合使用 tab栏部分 <view class"tabs"><view class"tab_item" v-for&…

Nvidia显卡驱动、CUDA、cuDNN、Anaconda及Tensorflow-GPU版本

Nvidia显卡驱动、CUDA、cuDNN、Anaconda及Tensorflow-GPU版本 一、确定版本关系二、安装过程1.安装显卡驱动2、安装CUDA3、安装cudnn4、安装TensorFlow 三、卸载 一、确定版本关系 1、CUDA与显卡驱动 https://www.nvidia.com/Download/index.aspx 2、cuDNN Toolkit与CUDA版本…

【雕爷学编程】Arduino动手做(12)---霍尔磁场传感器模块4

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

springboot第35集:微服务与flutter安卓App开发

Google Playplay.google.com/apps/publis…[1]应用宝open.qq.com/[2]百度手机助手app.baidu.com/[3]360 手机助手dev.360.cn/[4]vivo 应用商店dev.vivo.com.cn/[5]OPPO 软件商店&#xff08;一加&#xff09;open.oppomobile.com/[6]小米应用商店dev.mi.com/[7]华为应用市场dev…

摆动序列——力扣376

文章目录 题目描述贪心题目描述 贪心 int wiggleMaxLength(vector<int>& nums){int n=nums.

Stable Diffusion基础:ControlNet之人体姿势控制

在AI绘画中精确控制图片是一件比较困难的事情&#xff0c;不过随着 ControlNet 的诞生&#xff0c;这一问题得到了很大的缓解。 今天我就给大家分享一个使用Stable Diffusion WebUI OpenPose ControlNet 复制照片人物姿势的方法&#xff0c;效果可以参考上图。 OpenPose 可以…

将vsCode 打开的多个文件分行(栏)排列,实现全部显示,便于切换文件

目录 1. 前言 2. 设置VsCode 多文件分行(栏)排列显示 1. 前言 主流编程IDE几乎都有排列切换选择所要查看的文件功能&#xff0c;如下为Visual Studio 2022的该功能界面&#xff1a; 图 1 图 2 当在Visual Studio 2022打开很多文件时&#xff0c;可以按照图1、图2所示找到自…

《Linux从练气到飞升》No.11 初识操作系统

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

HTML详解连载(1)

这里写目录标题 HTML定义HTML 超文本标记语言标签语法注意拓展 HTML基本骨架解释VS Code 快速生成骨架&#xff1a;标签的关系父子关系&#xff08;嵌套关系&#xff09;兄弟关系&#xff08;并列关系&#xff09; 代码格式注释 标题标签标签名:h1-h6(双标签)经验&#xff1a; …

十二种排序算法(附动图)

排序算法 一、基本介绍 ​ 排序算法比较基础&#xff0c;但是设计到很多计算机科学的想法&#xff0c;如下&#xff1a; ​ 1、比较和非比较的策略 ​ 2、迭代和递归的实现 ​ 3、分而治之思想 ​ 4、最佳、最差、平均情况时间复杂度分析 ​ 5、随机算法 二、排序算法的分类 …

Linux系统目录结构介绍

Linux系统目录结构介绍 一、目录结构 Linux系统的目录结构是一颗倒状树&#xff1a; “/”表示最顶层的目录&#xff0c;叫做根目录。 &#xff08;1&#xff09;pwd可以显示当前所在的目录。 &#xff08;2&#xff09;cd可以切换当前的目录&#xff0c;例如&#xff0c;…

Jenkins部署python接口自动化测试

一、点击新建Item 二、指定源码和分支 私钥位置&#xff1a;C:\Users\Administrator\.ssh 文件下 三、构建脚本编写 四、构建后操作 指定输出的allure 结果目录

jmeter性能测试常见的一些问题

一、request 请求超时设置 timeout 超时时间是可以手动设置的&#xff0c;新建一个 http 请求&#xff0c;在“高级”设置中找到“超时”设置&#xff0c;设置连接、响应时间为2000ms。 1. 请求连接超时&#xff0c;连不上服务器。 现象&#xff1a; Jmeter表现形式为&#xff…

识别和应对内存抖动

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、案例分析3.1 使用memory-profiler3.2 使用 cp…

ArduPilot飞控之FAILSAFE机制

ArduPilot飞控之FAILSAFE机制 1. 源由2. 设计2.1 遥控信号丢失2.1.1 触发原因2.1.2 场景分析2.1.3 重要参数 2.2 电池低电2.2.1 触发原因2.2.2 场景分析2.2.3 重要参数 2.3 地面站/电传信号丢失2.3.1 触发原因2.3.2 场景分析2.3.3 重要参数 2.4 EKF故障(Extended Kalman Filter…

解密创意保护的钥匙:软著申请流程详解

引言 在当今数字化的浪潮下&#xff0c;软件已经渗透到我们生活的方方面面&#xff0c;从工作到娱乐&#xff0c;从沟通到创新。每一个看似简单的应用程序背后&#xff0c;都蕴藏着无限的创意和智慧。然而&#xff0c;这些创意往往也伴随着被复制、盗用甚至侵权的风险。为了保…

什么是BFC?它有什么作用?如何创建BFC?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是BFC⭐ BFC的作用⭐ 创建BFC的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web…

【100个 Unity实用技能】☀️ | Unity UGUI ScrollView滑动到指定位置

Unity 小科普 老规矩&#xff0c;先介绍一下 Unity 的科普小知识&#xff1a; Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案&#xff…

JMeter常用功能及使用方法详解

JMeter被广泛应用于软件性能测试&#xff0c;是一个开源的、纯Java编写的测试工具&#xff0c;其中包括了很多强大的功能。以下将重点介绍JMeter常用功能及使用方法。 一、JMeter压力测试 1.什么是JMeter压力测试&#xff1f; JMeter压力测试就是模拟多种负载条件并分析不同条…

【雕爷学编程】Arduino动手做(07)---电位器模块之电位器调速并带蜂鸣的8位跑马灯

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…