C++的六大“天选之子“拷贝构造与与运算符重载

news2024/11/27 6:12:49

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解C++中有关类和对象的介绍,本篇是中篇的第结尾篇文章,讲解拷贝构造,运算符重载以及取地址重载符.
金句分享:
✨别在最好的年纪,辜负了最好的自己.✨

一、“拷贝构造函数”

拷贝构造函数

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

2.1 自动生成的"拷贝构造函数"

假设哦我们需要创建两个一模一样的对象AB.
在这里插入图片描述
那我们可以先创建一个对象A,再通过将A作为参数,传给B进行初始化,
即一个自定义类型实例化出的对象(B)用另一个该类型实例化出的对象(A)进行初始化.

class Date
{
public:

	Date(int year = 2020, int month = 1, int day = 1)//全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date A(2023, 7, 20);
	A.Print();
	printf("\n");
	Date B(A);//会调用系统生成的拷贝构造
	B.Print();
	return 0;
}

运行结果:

2023-7-20

2023-7-20

其实拷贝构造函数就是构造函数的一种重载形式,他也是六大天选之子之一,没有显式定义时,编译器也会自动生成,但是只会完成"浅拷贝"(下面讲)…

2.2 自定义"拷贝构造函数"

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

class Date
{
public:

	Date(int year = 2020, int month = 1, int day = 1)//全缺省构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)//拷贝构造函数
	{
		cout << "拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 7, 20);
	d1.Print();
	printf("\n");
	Date d2(d1);
	d2.Print();
	return 0;
}

我们发现Date(const Date& d)这里使用了引用传参,如果直接传参会怎样呢?
在这里插入图片描述
为什么会报错呢?

void test(int a)
{

}

void test(Date d1)
{

}

int main()
{
	Date d1(2023, 7, 20);
	test(2);
	test(d1);
	return 0;
}

这段代码会调用Date 类的拷贝构造.
在这里插入图片描述
对于自定义类型作为参数时,必须调用该类型的拷贝构造函数.
所以可以回答上面的问题了.

在这里插入图片描述
所以拷贝构造函数传参时采用引用传参,这样就避免了传参时调用拷贝构造.

2.3 深浅拷贝?

前面在介绍编译器自动生成的"拷贝构造函数"时,提到了浅拷贝,那什么是浅拷贝呢?

浅拷贝:按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
在这里插入图片描述

深拷贝:
在这里插入图片描述

示例:
栈类中没有显式定义拷贝构造函数,编译器自动生成的拷贝构造是浅拷贝带来的问题.

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

typedef int DataType;
class Stack
{
public:
	Stack(int capacity=5)//全缺省构造函数
	{
		cout << "Stack" << endl;
		_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()//析构函数
	{
		cout << "~Stack"<< endl;
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *
				sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);//这条语句会报错.
	return 0;
}

运行结果:
在这里插入图片描述
原因:
因为编译器默认生成的拷贝构造是浅拷贝,这里两个对象的_array也就指向了同一块内存空间,但是两个对象的声生命周期结束时,会调用各自的析构函数,这也就导致对同一块空间进行了释放操作.
解决方法:
显示定义一个拷贝构造函数.

	Stack(const Stack& S)//深拷贝
	{
		_array = (int*)malloc(sizeof(int) * S._capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		memcpy(S._array,_array,sizeof(int)*S._size);
		_capacity = S._capacity;
		_size = S._size;
	}

总结:

拷贝构造使用场景:

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象
  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
  3. 当一个对象作为参数传递给函数时,拷贝构造函数会被调用来创建一个新的对象,该新对象与传递的对象具有相同的属性和属性值,但是它们在内存中是独立的。
  4. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝.

二、赋值运算符重载(“=”)

2.1 运算符重载的介绍

class Date//日期类
{
public:
	Date(int year = 2023, int month = 10, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void test1()
{
	Date d1(2023, 7, 28);
	Date d2;
	if (d2 == d1)
	{
		cout << "d1=d2";
	}
	if (d1 < d2)
	{
		cout << "d1<d2";
	}
}

在这里插入图片描述

自定义类型是无法像内置类型一样比较大小和使用一些常规运算符的.
为什么呢?
在这里插入图片描述
因为自定义类型是用户自己定义的,编译器不知道该如何进行比较.那编译器太笨了吧,日期按 年-月-日依次比较不就行了?
个人理解:

  1. 格局打开,如果是别的类呢?比如:person是按名字还是按职位,还是按什么?你不告诉编译器如何比较,编译器也很无奈,不敢瞎搞的.
  2. 编译器咋知道你year是年,要是牛牛用nian来命名,他也能识别出来是年吗?

综上,自定义类型如何进行运算比较,只有用户自己知道,所以用户需要自己来设计规则.

在这里插入图片描述
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型.

函数名:关键字operator+需要重载的运算符符号。
operator+ 需要重载的运算符

注意事项:

  • 不能通过连接其他符号来创建新的操作符:
    示例:operator@

  • 重载操作符必须有一个类类型参数
    运算符重载是通过类的成员函数或全局函数来实现的,而这些函数必须具有特定的参数列表。
    对于成员函数的重载操作符,至少需要一个类类型参数,它表示操作符的左操作数。例如,对于二元操作符(如 +、-、* 等),成员函数的参数列表通常还包括一个非常量引用或常量引用,表示操作符的右操作数。

  • 用于内置类型的运算符,其含义不能改变:
    例如:内置的整型*不要实现为了/,害人是不对的.

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

  • 注意以下5个运算符不能重载。“.*” (点星) 、" :: " sizeof ? : .

在C++中,有一些操作符是不能被重载的,包括以下几种情况:

  1. ::(作用域解析操作符):作用域解析操作符用于指定命名空间、类或结构的作用域,并访问其成员。它不能被重载,因为它的含义在语言中已经固定不可更改。

  2. .*(指针到成员操作符)和 ->*(指向成员指针的操作符):这些操作符用于访问类的成员指针。它们存储了一个指向类成员的指针,并用于在运行时访问该成员。它们也不能被重载。

  3. sizeof(大小操作符):sizeof操作符用于获取一个对象或类型的大小(以字节为单位)。它是一个编译时的操作符,不能在运行时被重载。因为在编译时就已经确定了对象或类型的大小。

  4. ?:(条件操作符,即三目运算符):条件操作符是一个三元操作符,用于根据条件选择不同的表达式。它不能被重载,因为它的语法和含义已经在语言中定义好了。

  5. .C++中,点操作符(“.”)是用来访问对象的成员的,而它本身是不能被重载的。点操作符的行为在语言中是固定的,无法通过重载来改变。

2.2 赋值运算符重载:

(1)编译器自动生成的 “赋值运算符重载”

class Date//日期类
{
public:
	Date(int year = 2023, int month = 10, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


void test1()
{
	Date d1(2023, 7, 28);
	Date d2;
	d1.print();
	d2.print();
	cout << endl;

	d2 = d1;

	d1.print();
	d2.print();

}
int main()
{
	test1();
	return 0;
}

在这里插入图片描述

赋值运算符只能重载成类的成员函数不能重载成全局函数:
原因:

赋值运算符如果不显式实现(自己定义),编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
在这里插入图片描述

那编译器会生成一个默认赋值运算符重载会做什么事情呢?

以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

当然对于日期类这种只需要浅拷贝的类来说,编译器默认生成就已经足够了,但是像stack类,同样引发深浅拷贝的问题.

三、最后的两个天选之子

哈哈哈,期待到最后的两个默认成员函数其实没什么要讲解的.

  1. 取地址操作符重载operator&()
  2. const取地址操作符重载operator&()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/858696.html

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

相关文章

甄知头条 | 燕千云与TeamCenter深度集成,无缝连接!打造协作利器

​ 甄知科技旗下的“燕千云数智化业务服务平台”近日与TeamCenter进行深度集成&#xff0c;形成合力&#xff0c;为使用TeamCenter软件的企业提供了更加全面、高效、智能的数字化转型解决方案。 Teamcenter&#xff0c;是全世界最广泛使用的PLM系统&#xff0c;是业内首个将单个…

奇安信天擎导致软件无法卸载 - 解决方案

奇安信天擎导致软件无法卸载 - 解决方案 前言安全模式解决方案进入安全模式卸载无法卸载的软件 前言 若电脑中安装了奇安信天擎&#xff0c;此软会时刻监控系统&#xff0c;禁止某些程序运行&#xff0c;开机自启且无法主动退出。但可能会出现这样一种情况&#xff0c;安装某软…

Android Studio实现滑动图片验证码

源代码链接 效果: MainActivity package com.example.slidingpattern;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.MotionEvent; i…

Django实现音乐网站 ⑽

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是后台对歌曲类型、歌单功能原有功能进行部分功能实现和显示优化。 目录 歌曲类型功能优化 新增编辑 优化输入项标题显示 父类型显示改为下拉菜单 列表显示 父类型显示名称 过滤器增加父类型 歌单表功能优化…

Centos7.9编写分布式集群大数据系统_自启动脚本---Linux工作笔记058

由于为了方便,系统在启动的时候,设置成系统所有的,集群软件都自动启动,这样,可以减少,运维成本,要不然,这么多,工具,有的还不知道放到什么位置了,一个个去启动,非常麻烦. 首先编辑: 编辑这个文件: 然后编辑这个文件,写上自己,自启动脚本的位置,然后保存 :wq 然后再给这个文件…

多技术融合下生态系统服务权衡与协同动态分析教程

详情点击链接&#xff1a;多技术融合下生态系统服务权衡与协同动态分析教程 前言 生态系统服务是指生态系统所形成的用于维持人类赖以生存和发展的自然环境条件与效用&#xff0c;是人类直接或间接从生态系统中得到的各种惠益。联合国千年生态系统评估&#xff08;Millennium…

连接不上linux上的redis

禁掉防火墙 在虚拟机上查看防火墙的状态 systemctl status firewalld.service 需要给他关停 systemctl stop firewalld.service systemctl disable firewalld.service 再次查看防火墙状态 这样也就连接成功了

普罗米修斯之一实现图形化监控

普罗米修斯之一实现图形化监控 1&#xff1a;prometheus1. 下载&#xff1a;2. 安装&#xff1a;3. 启动&#xff1a;1&#xff1a;启动方式之一加入systemctl2&#xff1a;启动方式之二---直接启动3&#xff1a;启动方式之三----后台运行 4&#xff1a;默认配置文件prometheus…

家政服务平台|家政上门服务系统打开时代新渠道

在快节奏的现代社会&#xff0c;工作和家庭的双重压力常常使人们备受折磨。为了缓解这种压力&#xff0c;我们公司推出了一款创新的家政上门服务系统&#xff0c;旨在为您提供便捷、高效的生活服务。通过结合先进技术和人性化服务&#xff0c;我们致力于改善您的生活品质&#…

水质分析积分球定义和原理

随着社会经济的快速发展&#xff0c;人们对水质的要求不断提升&#xff0c;特别是生活饮用水&#xff0c;检测项目渐趋完善。在工业化大发展的前提下&#xff0c;水资源正遭受着严重的污染&#xff0c;因此确保水质安全&#xff0c;定期开展对饮用水的检测工作已迫在眉睫。环境…

Maven 生成(打包)带有依赖的可以直接执行的一个 jar 包

在pom中增加如下内容 <build><plugins><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><archive><manifest><mainClass>com.example.xxx.YourClass</mainClass></manifest></…

非常强大的IDEA代码重构小技巧

文章目录 命名相关refactor thisrenamechange signature 变量相关extract/introduce 移动move instance methodcopy classsafe delete 成员方法与类型Type migration 使用接口与取消继承 idea是个非常强大的ide&#xff0c;尤其是在重构时&#xff0c;提供了非常强大的功能。下…

twisted 18.7.0 requires PyHamcrest>=1.9.0 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

火爆全网,HttpRunner自动化测试框架-CSV文件数据(详细总结)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 当数据量比较大的…

设计模式(4)装饰模式

一、介绍&#xff1a; 1、应用场景&#xff1a;把所需的功能按正确的顺序串联起来进行控制。动态地给一个对象添加一些额外的职责&#xff0c;就增加功能来说&#xff0c;装饰模式比生成子类更加灵活。 当需要给一个现有类添加附加职责&#xff0c;而又不能采用生成子类的方法…

逆向破解学习-雷电星海战歌

apk 雷电星海战歌 https://download.csdn.net/download/AdrianAndroid/88200826 安装apk&#xff0c;并试玩 # 通过关键字搜索jad 找到统一支付接口 找到匿名内部类的名称 Hook代码 public class HookComAstPlane extends HookImpl {Overridepublic String packageNam…

15.4 【Linux】可唤醒停机期间的工作任务

15.4.1 什么是 anacron anacron 并不是用来取代 crontab 的&#xff0c;anacron 存在的目的就在于我们上头提到的&#xff0c;在处理非24 小时一直启动的 Linux 系统的 crontab 的执行&#xff01; 以及因为某些原因导致的超过时间而没有被执行的调度工作。 其实 anacron 也是…

makefile include 使用介绍

文章目录 前言一、include 关键字1. 语法介绍2. 处理方式示例&#xff1a; 二、- include 操作总结 前言 一、include 关键字 1. 语法介绍 在 Makefile 中&#xff0c;include 指令&#xff1a; 类似于 C 语言中的 include 。将其他文件的内容原封不动的搬入当前文件。 当 …

云仓酒庄给予经销商支持和帮助,为服务好终端架起桥梁

随着经济消费持续复苏&#xff0c;白酒市场也在快速回暖&#xff0c;据相关数据显示&#xff0c;白酒行业营收利润的持续增长&#xff0c;促使不少经销商走入白酒市场这一赛道&#xff0c;品牌经销成为瓜分“蛋糕”的重要方式。同时&#xff0c;不少专注于白酒技术研发、酿造、…

【从0开始离线数仓项目】——数据仓库的环境搭建(1)

目录 一、服务器环境准备 1.2 编写集群分发脚本xsync 1.3 SSH无密登录配置 1.4 JDK准备 1.5 环境变量配置说明 二、集群所有进程查看脚本 三、Zookeeper安装 3.1 分布式安装部署 3.2 ZK集群启动停止脚本 3.3 客户端命令行操作 一、服务器环境准备 CentOS 7 怎么从命…