类与对象(二)--类的六个默认成员函数超详细讲解

news2024/11/18 20:01:32

目录

1.类的默认六个成员函数✒️ 

2.构造函数

2.1构造函数的概念✒️ 

2.2构造函数的特性✒️ 

 3.析构函数

3.1析构函数的概念✒️ 

3.2析构函数的特征✒️ 

4.拷贝构造函数

4.1拷贝构造函数的概念✒️ 

 4.2拷贝构造函数的特征✒️ 

4.3思考❓

4.4深拷贝和浅拷贝⭐️✒️ 

4.4.1浅拷贝⭐️✒️ 

4.4.2深拷贝⭐️✒️

4.4.3总结✒️✒️

5.赋值运算符重载

5.1什么叫赋值运算符重载?✒️ 

5.2赋值运算符重载✒️ 

5.3 前置++和后置++重载✒️ 

6.const成员✒️ 

6.1思考❓

 7.取地址及const取地址操作符重载✒️ 


1.类的默认六个成员函数✒️ 

🔎什么是默认的成员函数?当我们创建一个类,如果没有显式的定义以下六个成员函数,编译器会为我们自动生成这些函数。

🔥.构造函数

🔥析构函数

🔥.拷贝构造函数

🔥.赋值重载函数

🔥.普通对象取地址重载函数

🔥.const对象取地址重载函数

✋也就意味着,即使我们创建一个空类,编译器也自动生成这六个成员函数,只不过是隐式的

2.构造函数

2.1构造函数的概念✒️ 

🔎构造函数是一种特殊的成员函数,用于初始化类的对象。构造函数在对象被创建时自动调用,确保对象在使用之前处于合适的状态。

观察以下代码🚦

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;
}

 ✋上述代码中的Date类可以通过Init成员函数(公有方法)给对象设置日期,但是如果我们每次创建对象的时候都要调用这个函数的话就显得比较麻烦。构造函数就能很好的解决这个问题。因为构造函数在对象在创建的时候由编译器自动调用,并且在对象的整个生命周期只调用一次

⭐️构造函数是一个特殊的成员函数,其函数名和类名相同,且没有返回值

2.2构造函数的特性✒️ 

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象

特性:

1️⃣. 函数名与类名相同。

2️⃣. 无返回值。void类型也不是

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

4️⃣. 构造函数可以重载。也就意味着可以有多个函数名相同参数不同的构造函数。

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

✋上述代码中通过无参构造创建了对象d1,对象后面不需要加括号,如果加了括号就变成了

Date d1() ,编译器会认为这行代码是在声明一个返回值为Date的函数。

观察以下代码🚦

⁉️为什么我将不带参数的构造函数注释掉后创建对象d1会报错呢?编译器不是会自动生成不带参数的构造函数吗?

上面我们已经提到,如果我们已经显式的定义构造函数那么编译器就不会再生成构造函数。✋就像上述代码中,我们其实已经显示定义了构造函数date(int y,iny m,int d),编译器就不会再生成任何的构造函数,也就不会生成Date()。当Date d1创建d1这个对象时编译器找不到Date()这个函数,也就会报错了。

 6️⃣.成员初始化列表。构造函数可以使用成员初始化列表,在构造函数体之前对成员进行初始化

7️⃣.编译器默认生成的构造函数在初始化成员变量时,对于自定义类型变量调用它们自己的构造函数,对于内置类型变量则不做处理。

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。

观察以下代码🚦

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

8️⃣.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

⭐️注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

 3.析构函数

3.1析构函数的概念✒️ 

🔎跟构造函数相反,析构函数是用来对对象被销毁时清理的。但是值得注意的是,析构函数并不是完成对对象本身的销毁,局部对象的销毁工作是由编译器完成的。当对象被销毁的时候,会自动调用该对象的析构函数,完成对对象资源的具体清理,一般是动态资源。析构函数是最后一次使用对象执行的动作。

3.2析构函数的特征✒️ 

特征:

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

2️⃣. 无参数无返回值类型

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

跟构造函数不同,析构函数必须无参。

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

5️⃣.类中成员的销毁过程依赖成员的数据类型。对于内置类型系统会自动回收,不需要析构函数。但是对于自定义类型,编译器就会调用该成员本身的析构函数

✋对于上面代码,Date类中有Time类类型的变量,当调用Date类的析构函数时,也会调用Time的析构函数。

6️⃣.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时(比如new在堆上开辟的空间),一定要写,否则会造成资源泄漏(内存泄漏)

✋上述类对象在初始化构造的时候会调用构造函数,用new开辟动态空间,如果我们不定义析构函数对该动态资源进行处理,默认生成的析构函数也不会回收,这样一来就会造成内存泄漏。

4.拷贝构造函数

4.1拷贝构造函数的概念✒️ 

🔎拷贝构造函数是用来用一个同类对象作为模板,生成构造一个一模一样新的对象。

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

 4.2拷贝构造函数的特征✒️ 

1️⃣. 拷贝构造函数是构造函数的一个重载形式。

2️⃣. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

3️⃣.如果没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。默认的拷贝构造函数对于内置类型变量按字节完成拷贝,也叫浅拷贝(值拷贝)。对于自定义类型变量则调用其拷贝构造函数完成拷贝。

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

举例🚦

✋上面代码中A类成员有一个指针arr指向了一片动态空间。如果启用编译器默认的拷贝构造函数进行值拷贝的话,会将指针的值再复制一遍,也就意味着与复制出来的指针指向了同一片地址。这样一来就会非常危险,因为当需要销毁对象a和对象a1的时候会调用其析构函数,也就会对同一片空间释放两次,这是不被允许的。

5️⃣. 拷贝构造函数典型调用场景🚦

🔥 使用已存在对象创建新对象

🔥 函数参数类型为类类型对象

🔥 函数返回值类型为类类型对象

4.3思考❓

 观察以下代码思考其构造函数,析构函数的调用顺序以及输出结果🚦

✋其实Test函数返回值的时候也会进行拷贝构造,但是现在很多编译器都将这一步优化掉了,所以也就看不到了。不同的编译器对以上代码的输出结果可能会有差别,比如在linux中的g++工具编译此代码的析构顺序就是3、2、1。

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

4.4深拷贝和浅拷贝⭐️✒️ 

🔎深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在对象拷贝过程中涉及的两个概念,主要关注于如何处理对象的成员变量(尤其是指针类型的成员变量)。

❓上面讲到,编译器自动生成的拷贝构造函数对对象的拷贝方式是浅拷贝,也就是按值拷贝。那么什么又是深拷贝?二者的区别在哪呢?

4.4.1浅拷贝⭐️✒️ 

  • 浅拷贝只是简单地复制对象的值,包括成员变量。如果对象包含指针,那么浅拷贝只是复制指针的值,而不是复制指针所指向的内容。
  • 对象的拷贝和原始对象共享相同的资源(如动态分配的内存区域),这可能导致潜在的问题,因为一个对象的修改可能会影响到另一个对象(之前出现的对一片空间释放两次)
  • 默认情况下,C++ 的复制构造函数和赋值运算符执行的是浅拷贝。

4.4.2深拷贝⭐️✒️

  • 深拷贝会复制对象的值,同时为对象的指针类型成员变量分配新的内存,并复制指针所指向的内容。这样,原始对象和拷贝对象将拥有独立的资源,对一个对象的修改不会影响另一个对象。
  • 深拷贝需要程序员显式实现,通常涉及到复制构造函数、赋值运算符或者自定义的拷贝逻辑。

4.4.3总结✒️✒️

  • 浅拷贝简单快速,但容易引发潜在的问题,特别是当对象包含动态分配的资源时。
  • 深拷贝较为安全,但由于需要额外的内存分配和复制操作,可能效率较低。在实现深拷贝时需要小心管理资源,防止内存泄漏等问题。

5.赋值运算符重载

5.1什么叫赋值运算符重载?✒️ 

🔎C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。运算符重载允许程序员自定义特定运算符的操作,以适应用户定义的类型。

🚩函数名字为:关键字operator后面接需要重载的运算符符号

注意🚦

🔥不能通过连接其他符号来创建新的操作符:比如operator@

🔥重载操作符必须有一个类类型参数

🔥用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义

🔥作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

🔥 .*”  “::”  “sizeof”  “?:”  “.” 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

 举例🚦

🚩我们先定义一个日期类,再重载一个比较日期类是否相等的运算符==

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	 bool operator==(const Date& d2)
	{
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void Test()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	cout << (d1 == d2) << endl;//因为没有重载<<,所以需要用括号括起来
	cout << d1.operator==(d2) << endl;//调用成员函数的形式也可以
}

✋上述代码中的成员函数 operator==()会有一个隐式的this指针,所以在声明的时候只需要再设一个形参就可以了。

5.2赋值运算符重载✒️ 

1️⃣.赋值运算符重载格式

🔥参数类型:const T&,传递引用可以提高传参效率

🔥返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

🔥检测是否自己给自己赋值

🔥返回*this :要复合连续赋值的含义

 Date& operator=(const Date& d) {
	 if (this != &d) {//如果参数是对象本身,那就不用继续赋值
		 _year = d._year;
		 _month = d._month;
		 _day = d._day;
	 }
	 return *this;
 }

2️⃣.赋值运算符只能重载成类的成员函数不能重载成全局函数

✋✋上面我们已经讲过,赋值运算重载函数是默认成员函数,如果我们不在类中显式定义那么编译器就会自动生成一个赋值运算符重载函数。这样一来如果我们再全局定义了也一个赋值运算重载函数,就会和编译器生成的重载函数起冲突。所以,赋值运算符重载函数只能定义成类成员。

🚩在《C++ prime》第5版p500页有提到

3️⃣. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝(拷贝而不是拷贝构造)。

注意🚦内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。 

4️⃣如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

⭐️跟上面拷贝构造函数需要自己显式定义的道理是一样的,如果类中涉及到资源的管理,比如有一个指针arr维护了一片动态空间。这样的类就需要自己实现一个赋值重载函数,不然靠编译器自动生成的函数只会复制一个相同值的指针,也就是指向同一片空间的指针,这样就容易发生错误。

5.3 前置++和后置++重载✒️ 

🔎通过上面对运算符重载的学习,我们知道对于一个操作符来说,顺序非常重要,就像i++和++i的含义是不一样的。前置++和后置++重载又该怎么写呢?

前置++

对于一个日期类的对象来说,+1其实就是往后加一天。

Date& operator++() {//前置++
	_day++;
	return *this;
}

✋返回+1之后的结果 ,this指向的对象函数结束后不会销毁,故以引用方式返回提高效率

⭐️后置++

前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载

C++规定后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。

	Date operator++(int) {//后置++
		Date temp(*this);
		_day++;
		return temp;
	}

后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存 一份,然后给this+1。又temp是临时对象,因此只能以值的方式返回,不能返回引用。

6.const成员✒️ 

🔎将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。 

🔎由于我们的类里this指针是隐式的,想要用const修饰this指针该怎么办呢?

🔎c++规定,若在类里面的成员函数想用const修饰this指针,就将const写在函数括号的外面。

举例🚦

//显示日期
void Dispaly()const {
	cout << _year << "-" << _month << "-" << _day << endl;
}

6.1思考❓

1 、const对象可以调用非const成员函数吗?

💡不能,权限放大。

2、非const对象可以调用const成员函数吗?

💡可以,权限变小

3、const成员函数内可以调用其它的非const成员函数吗?

💡不能,权限放大

4、非const成员函数内可以调用其它的const成员函数吗?

💡能,权限变小

 7.取地址及const取地址操作符重载✒️ 

 🔎这两个重载一般都不用自己重新定义,用编译器自动生成的就够了

class Date
{ 
public :
 Date* operator&()
 {
 return this ;
}
 
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};

✋ 这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需 要重载,比如想让别人获取到指定的内容

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

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

相关文章

嵊州吃喝玩乐篇

1. 豆腐馒头 胖大姐豆腐馒头&#xff08;推荐&#xff09; 地址: 嵊州市越秀路96-98号 高德地图搜 “胖大姐黄泽豆腐馒头” 黄泽豆腐馒头 地址&#xff1a;嵊州市越秀路111号 高德地图搜 “嵊州市玉英豆腐馒头店” 2. 烧烤 老张烧烤&#xff08;推荐&#xff09; 地址&a…

3.5作业

1.使用write 和 read 实现 文件夹拷贝功能&#xff0c;不考虑递归拷贝 #include<stdio.h> #include<string.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main(in…

#stm32学习总结GPIO

1、 GPIO 1.1、简介 就是通用输入输出口 2&#xff0c; STM32 GPIO简介&#xff08;了解&#xff09; 2.1&#xff0c;GPIO特点 1&#xff0c;不同型号&#xff0c;IO口数量可能不一样&#xff0c;可通过选型手册快速查询 2&#xff0c;快速翻转&#xff0c;每次翻转最快只…

PTA L2-009 抢红包

题目&#xff1a; 没有人没抢过红包吧…… 这里给出N个人之间互相发红包、抢红包的记录&#xff0c;请你统计一下他们抢红包的收获。 输入格式&#xff1a; 输入第一行给出一个正整数N&#xff08;≤104&#xff09;&#xff0c;即参与发红包和抢红包的总人数&#xff0c;则…

【网站项目】121开放式教学评价管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

EtherCAT运动控制器在ROS上的应用(上)

本文以正运动技术EtherCAT运动控制器ZMC432和ZMC408CE为例&#xff0c;介绍正运动技术运动控制器在ROS上的应用开发。 在正式学习之前&#xff0c;我们先了解一下正运动技术的EtherCAT运动控制器ZMC432和ZMC408CE。这两款产品分别是32轴&#xff0c;8轴EtherCAT运动控制器。 …

分享Web.dev.cn中国开发者可以正常访问

谷歌开发者很高兴地宣布&#xff0c;web.dev 和 Chrome for Developers 现在都可以通过 .cn 域名访问&#xff0c;这将帮助中国的开发者更加容易获取我们的内容。 在 .cn 域名上&#xff0c;我们已向您提供所有镜像后的内容&#xff0c;并提供支持的语言版本。 Web.dev 中国开…

【Unity】使用ScriptableObject存储数据

1.为什么要用ScriptableObject&#xff1f; 在游戏开发中&#xff0c;有大量的配置数据需要存储&#xff0c;这个时候就需要ScriptableObject来存储数据了。 很多人会说我可以用json、xml、txt&#xff0c;excel等等 但是你们有没有想过&#xff0c;假设你使用的是json&#x…

CSS全局样式的设置,JavaScript运算符

面试题&#xff1a; Html 1&#xff0c;html语义化 2&#xff0c;meta viewport相关 3&#xff0c;canvas 相关 CSS 1&#xff0c;盒模型 1.1&#xff0c;ie盒模型算上border、padding及自身&#xff08;不算margin&#xff09;&#xff0c;标准的只算上自身窗体的大小 c…

相机类型的分辨率长宽、靶面尺寸大小、像元大小汇总

镜头的靶面尺寸大于等于相机靶面尺寸。 相机的芯片长这样&#xff0c;绿色反光部分&#xff08;我的手忽略&#xff09;&#xff1a; 基本所有像素的相机的靶面大小都可以在这个表格里面找到。 镜头的靶面尺寸在镜头外表上可以找到&#xff0c;选型很重要&#xff01;

Android logcat系统

一 .logcat命令介绍 android log系统: logcat介绍 : logcat是android中的一个命令行工具&#xff0c;可以用于得到程序的log信息. 二.C/Clogcat访问接口 Android系统中的C/C日志接口是通过宏来使用的。在system/core/include/android/log.h定义了日志的级别&#xff1a; /…

信息安全系列04-安全启动介绍

本文框架 1. 基本概念1.1 基本概念回顾1.2 数字签名及验签流程 2. 安全启动实施2.1 信任根选择2.1.1 使用HSM作为信任根2.1.2 使用最底层Bootloader作为信任根 2.2 校验方法确认2.2.1 基于非对称加密算法&#xff08;数字签名&#xff09;2.2.2 基于对称加密算法 2.3 安全启动方…

三星成功研发出业界首款12层堆叠HBM3E

三星电子有限公司成功研发出业界首款12层堆叠HBM3E DRAM——HBM3E 12H&#xff0c;这是迄今为止容量最大的HBM产品。这款新型HBM3E 12H内存模块提供了高达1,280GB/s的史上最高带宽&#xff0c;并拥有36GB的存储容量&#xff0c;相较于之前的8层堆叠HBM3 8H&#xff0c;在带宽和…

Java代码审计安全篇-常见Java SQL注入

前言&#xff1a; 堕落了三个月&#xff0c;现在因为被找实习而困扰&#xff0c;着实自己能力不足&#xff0c;从今天开始 每天沉淀一点点 &#xff0c;准备秋招 加油 注意&#xff1a; 本文章参考qax的网络安全java代码审计&#xff0c;记录自己的学习过程&#xff0c;还希望…

怎么把一段音频的人声和背景音乐分开?3种方法分享

怎么把一段音频的人声和背景音乐分开&#xff1f;随着技术的不断发展进步&#xff0c;将音频中的人声和背景音乐分离已成为现实。这种技术不仅提升了音频编辑的效率和准确性&#xff0c;更为我们提供了无限的可能性。例如&#xff0c;音乐制作人可以更容易地提取出纯净的人声或…

【比较mybatis、lazy、sqltoy、mybatis-flex、easy-query、mybatis-mp操作数据】操作批量新增、分页查询(四)

orm框架使用性能比较 比较mybatis、lazy、sqltoy、mybatis-flex、easy-query、mybatis-mp操作数据 环境&#xff1a; idea jdk17 spring boot 3.0.7 mysql 8.0测试条件常规对象 orm 框架是否支持xml是否支持 Lambda对比版本编码方式mybatis☑️☑️3.5.4lambda xml 优化sq…

SAR ADC学习笔记(3)

一、SAR ADC采样电路 1.采样网络的时域响应&#xff1a;采保信号 2.采样网络的KT/C噪声 3.采样抖动 采样开关的种类 1.单MOS管开关 2.传输门开关 3.栅极自举&#xff08;Bootstrap&#xff09;开关 结论&#xff1a;M4的衬底需要和B点短接&#xff0c;保证B点能够到达高压&…

Linux系统的服务/进程

系统守护进程&#xff08;服务&#xff09; •服务就是运行在网络服务器上监听用户请求的进程 •服务是通过端口号来区分的 常见的服务及其对应的端口 1.ftp&#xff1a;21 FTP指的是文件传输协议&#xff0c;它是用于在计算机网络上进行文件传输的标准网络协议。通过FTP&am…

为什么MySQL中多表联查效率低,连接查询实现的原理是什么?

MySQL中多表联查效率低的原因主要涉及到以下几个方面&#xff1a; 数据量大: 当多个表通过连接查询时&#xff0c;如果这些表的数据量很大&#xff0c;那么查询就需要处理更多的数据&#xff0c;这自然会降低查询效率。 连接操作复杂性: 连接查询需要对参与连接的每个表中的数…

ES入门三:全文搜索Api实践

ES是一个强大的搜索引擎&#xff0c;它提供了非常丰富的数据检索Api来满足用户各种各样的需求。我们今天要介绍的是部分非常基础的全文搜索Api&#xff0c;这部分Api我们会在日常使用中经常用到。 在我们查询一些文本内容的时候&#xff0c;一般不会做精确匹配&#xff0c;一来…