C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍

文章目录

  • 前言
  • 一、拷贝构造函数
    • 1. 概念
    • 2. 特征
    • 3. 编译器生成默认拷贝构造函数
    • 4. 拷贝构造函数典型使用场景
  • 二、运算符重载函数
  • 三、赋值运算符重载函数
    • 1. 赋值运算符重载格式
    • 2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
    • 3.编译器生成一个默认赋值运算符重载
  • 四、前置++和后置++重载
  • 总结


前言

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍


一、拷贝构造函数

在实际运用中,我们有时候需要操作一个实例化好的类,但是又不想改变这个类,这时候,我们就需要拷贝构造函数。

1. 概念

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

2. 特征

拷贝构造函数也是特殊的成员函数,其特征如下:

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

拷贝构造函数是构造函数的一个重载形式,所以拷贝构造函数的函数名与类名相同,参数与构造函数不同,无返回值。

如下图:

  • 采用传值调用,会引发无穷递归,编译器强制检查报错。
    在这里插入图片描述

  • 应该采用类类型对象的引用,如下:

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 6, 22);

	Date d2(d1);


	return 0;
}

在这里插入图片描述

3. 编译器生成默认拷贝构造函数

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

简单来说就是,不显示定义,编译器默认生成一个构造函数

  • 对于内置类型会进行值拷贝(浅拷贝)。
  • 对于自定义类型会调用它的拷贝构造函数。
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 9, 1);

	Date d2(d1);


	return 0;
}

在这里插入图片描述

编译器默认生成的拷贝构造函数是值拷贝(浅拷贝),但是有些情况下我们需要深拷贝。比如:
对一个栈,我们不能直接浅拷贝,程序会崩溃。原因如下:

浅拷贝会将一个栈动态申请的空间的地址拷贝个另一个栈的指针,所以两个栈对象的指针指向同一块空间,析构函数会对同一块空间释放两次,程序会崩溃。

#include <iostream>
using namespace std;

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		_top = 0;
		_capacity = capacity;
	}

	void Push(int x)
	{
		// 增容
		_a[_top] = x;
		_top++;

	}

	~Stack()
	{
		if (_top)
		{
			free(_a);
			_a = nullptr;
			
			_top = 0;
			_capacity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1(10);
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);


	Stack st2(st1);

	return 0;
}

在这里插入图片描述


在这里插入图片描述

  • 所以,这种Stack的类对象,我们应该自己显示定义拷贝构造函数。
#include <iostream>
using namespace std;

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		_top = 0;
		_capacity = capacity;
	}

	Stack(const Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		memcpy(_a, st._a, sizeof(int) * st._top);

		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(int x)
	{
		// 增容
		_a[_top] = x;
		_top++;

	}

	~Stack()
	{
		if (_top)
		{
			free(_a);
			_a = nullptr;
			
			_top = 0;
			_capacity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1(10);
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);


	Stack st2(st1);

	return 0;
}

在这里插入图片描述

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

4. 拷贝构造函数典型使用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

二、运算符重载函数

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .*、 ::、 sizeof 、?: . 注意以上5个运算符不能重载**
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}


	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
//private:
	int _year;
	int _month;
	int _day;
};


bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}


bool operator>(const Date& x1, const Date& x2)
{
	if (x1._year > x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month > x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day > x2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	cout << operator<(d1, d2) << endl;
	cout << (d1 < d2) << endl;

	cout << operator>(d1, d2) << endl;
	cout << (d1 > d2) << endl;



	return 0;
}

在这里插入图片描述

使用运算符重载时,调用运算符重载函数,也可以直接使用运算符比较。

	cout << operator<(d1, d2) << endl;
	cout << (d1 < d2) << endl;

	cout << operator>(d1, d2) << endl;
	cout << (d1 > d2) << endl;
  • 一般建议直接使用运算符比较。

上述运算符重载到了全局中,运算符重载也可以重载到类中。

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator<(const Date& x2)
	{
		if (_year < x2._year)
		{
			return true;
		}
		else if (_year == x2._year && _month < x2._month)
		{
			return true;
		}
		else if (_year == x2._year && _month == x2._month && _day < x2._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	cout << d1.operator<(d2) << endl;
	cout << (d1 < d2) << endl;

	return 0;
}

在这里插入图片描述

三、赋值运算符重载函数

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	//d2.operator=(d1);

	d2 = d1;

	return 0;
}

在这里插入图片描述

赋值运算符可以选择调用赋值运算符重载函数,也可以直接使用赋值运算符,一般建议直接使用赋值运算符。

	d2.operator=(d1);
	d2 = d1;

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

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}


	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
//private:
	int _year;
	int _month;
	int _day;
};


Date& operator=(Date& left, const Date& right)
{
	left._year = right._year;
	left._month = right._month;
	left._day = right._day;

	return left;
}

在这里插入图片描述

3.编译器生成一个默认赋值运算符重载

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

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

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

四、前置++和后置++重载

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1328, int month = 1, int day = 4)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp(*this);
		_day += 1;
		return tmp;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	Date d1(1949, 10, 1);

	d = d1++; // d: 1949 10 1   d1: 1949 10 2
	
	d = ++d1; // d: 1949 10 3   d1: 1949 10 3

	return 0;
}
  • 前置++
    前置++:返回+1之后的结果
    注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
  • 后置++
    前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
    C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
    注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1而temp是临时对象,因此只能以值的方式返回,不能返回引用

总结

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍。

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

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

相关文章

WebSocket走私实践(附赠LiveGBS监控系统未授权管理员密码重置)

WebSocket走私实践&#xff08;附赠LiveGBS监控系统未授权管理员密码重置&#xff09; 对此&#xff0c;我特别感谢TryHackMe和HackTheBox academy&#xff0c;永远相信和追随英国TryHackMe所教导的网络安全知识,并保持学习 WebSocket走私相关的知识在这里 前段时间学习过htt…

Spring的自动注入(也称为自动装配)

自动注入&#xff08;也称为自动装配&#xff09;是Spring框架中的一个核心概念&#xff0c;它与手动装配相对立&#xff0c;提供了一种更简洁、更灵活的方式来管理Bean之间的依赖关系。 在Spring应用程序中&#xff0c;如果类A依赖于类B&#xff0c;通常需要在类A中定义一个类…

[Redis]缓存常见问题解决(缓存穿透、击穿、雪崩一文解决!通俗易懂、代码实战!手把手教你解决缓存问题三兄弟!)

Redis常见问题解决 要求 只用一种缓存技术&#xff0c;从实验点中挑一些试验进行试验原理。 1.缓存原理 目标&#xff1a;理解缓存的基本原理和工作机制。 实验步骤&#xff1a; 阅读各缓存技术机制的文档和官方资料。实现一个简单的应用程序&#xff0c;模拟数据的读写和…

ffmpeg+nginx+video实现rtsp流转hls流,web页面播放

项目场景&#xff1a; 最近调试海康摄像头需要将rtsp流在html页面播放,因为不想去折腾推拉流&#xff0c;所以我选择ffmpeg转hls流&#xff0c;nginx转发&#xff0c;html直接访问就好了 1.首先要下载nginx和ffmpeg 附上下载地址&#xff1a; nginx nginx news ffmpeg htt…

Java开发-面试题-0006-DELETE、VACUUM和TRUNCATE的区别

Java开发-面试题-0006-DELETE、VACUUM和TRUNCATE的区别 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; …

TF-IDF、BM25传统算法总结

1. TF-IDF算法 F-IDF&#xff08;词频-逆文档频率&#xff09;是一种用于衡量文本中词语重要性的方法&#xff0c;特别适用于信息检索和文本挖掘任务。下面会拆分为两部分深入讲解TF-IDF的计算过程&#xff0c;以便更好地理解。 TF-IDF的计算过程可以分为两个主要部分&#xf…

Butter Knife 8

// 部分代码省略… Override public View getView(int position, View view, ViewGroup parent) { ViewHolder holder; if (view ! null) { holder (ViewHolder) view.getTag(); } else { view inflater.inflate(R.layout.testlayout, parent, false); holder new ViewHolde…

Ansible 自动化运维实践

随着 IT 基础设施的复杂性不断增加&#xff0c;手动运维已无法满足现代企业对高效、可靠的 IT 运维需求。Ansible 作为一款开源的自动化运维工具&#xff0c;通过简洁易用的 YAML 语法和无代理&#xff08;agentless&#xff09;架构&#xff0c;极大简化了系统配置管理、应用部…

物理层(二)

2.2 传输介质 2.2.1 双绞线、同轴电缆、光纤和无线传输介质 传输介质也称传输媒体&#xff0c;是数据传输系统中发送器和接收器之间的物理通路。传输介质可分为:①导向传输介质&#xff0c;指铜线或光纤等&#xff0c;电磁波被导向为沿着固体介质传播:②)非导向传输介质&…

6月27日云技术研讨会 | 中央集中架构新车型功能和网络测试解决方案

会议摘要 “软件定义汽车”新时代下&#xff0c;整车电气电气架构向中央-区域集中式发展已成为行业共识&#xff0c;车型架构的变革带来更复杂的整车功能定义、更多的新技术的应用&#xff08;如SOA服务化、TSN等&#xff09;和更短的车型研发周期&#xff0c;对整车和新产品研…

【yolov8语义分割】跑通:下载yolov8+预测图片+预测视频

1、下载yolov8到autodl上 git clone https://github.com/ultralytics/ultralytics 下载到Yolov8文件夹下面 另外&#xff1a;现在yolov8支持像包一样导入&#xff0c;pip install就可以 2、yolov8 语义分割文档 看官方文档&#xff1a;主页 -Ultralytics YOLO 文档 还能切…

GLM-4V模型学习

智谱AI引领技术前沿&#xff0c;推出了新一代预训练模型GLM-4系列&#xff0c;其中的GLM-4-9B作为开源版本&#xff0c;展现了其在人工智能领域的深厚实力。在语义理解、数学运算、逻辑推理、代码编写以及广泛知识领域的数据集测评中&#xff0c;GLM-4-9B及其人类偏好对齐的版本…

AI音乐模型:创新还是颠覆?

文章目录 AI音乐大模型的崛起音乐创作门槛的降低与兴奋AI音乐作品的版权归属问题创意产业在AI阴影下的生长结语 &#x1f389;欢迎来到AIGC人工智能专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&…

Apple - Text System Storage Layer Overview

本文翻译整理自&#xff1a;Text System Storage Layer Overview&#xff08;更新日期&#xff1a;2012-09-19 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextStorageLayer/TextStorageLayer.html#//apple_ref/doc/uid/10000087i 文章目录 …

java:JWT的简单例子

【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.12.RELEASE</version> </dependency> <dependency><groupId>org.springf…

程序猿成长之路之数据挖掘篇——决策树分类算法(1)——信息熵和信息增益

决策树不仅在人工智能领域发挥着他的作用&#xff0c;而且在数据挖掘中也在分类领域中独占鳌头。了解决策树的思想是学习数据挖掘中的分类算法的关键&#xff0c;也是学习分类算法的基础。 什么是决策树 用术语来说&#xff0c;决策树&#xff08;Decision Tree&#xff09;是…

STM32驱动-ads1112

汇总一系列AD/DA的驱动程序 ads1112.c #include "ads1112.h" #include "common.h"void AD5726_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE );//PORTA、D时钟使能 G…

SQLite数据库(数据库和链表双向转换)

文章目录 SQLite数据库一、SQLite简介1、SQLite和MySQL2、基于嵌入式的数据库 二、SQLite数据库安装三、SQLite的常用命令四、SQLite的编程操作1、SQLite数据库相关API&#xff08;1&#xff09;头文件&#xff08;2&#xff09;sqlite3_open()&#xff08;3&#xff09;sqlite…

Springboot拓展之整合邮件 JavaMail的使用与实操

邮件 电子邮件仍然是我们企业间交往的一种非常常见的方式 发送简单邮件 第一步首先导入坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><version>2.6.13</version&…

架构师指南:现代 Datalake 参考架构

这篇文章的缩写版本于 2024 年 3 月 26 日出现在 The New Stack 上。 旨在最大化其数据资产的企业正在采用可扩展、灵活和统一的数据存储和分析方法。这一趋势是由企业架构师推动的&#xff0c;他们的任务是制定符合不断变化的业务需求的基础设施。现代数据湖体系结构通过将数…