C++【类和对象】(拷贝构造与运算符重载)

news2024/9/28 15:29:19

1. 拷贝构造

如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。

注意:拷贝构造是用一个已经实例化的对象来初始化一个新对象。

#include<iostream>
using namespace std;

class Date
{
public:

	Date(int year = 1, int month = 1, int day = 1)//初始化对象
	{
		cout << "这是构造函数" << endl;
		_year = year;
		_month = month;
		_day = day;
	}

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

在这里插入图片描述

拷贝构造的特点

  1. 拷贝构造函数是构造函数的⼀个重载

  2. 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值。

  3. C++规定:自定义类型对象进行拷贝行为必须调用拷贝构造,自定义类型传值传参和传值返回都会调用拷用构造完成,如果拷贝构造的参数就是传值,那么就会一直生成拷贝构造。
    在这里插入图片描述在这里插入图片描述

  4. 若未显式定义拷贝构造,编译器会自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。

#include<iostream>
using namespace std;

class Date
{
public:

	Date(int year = 1, int month = 1, int day = 1)
	{
		cout << "这是构造函数" << endl;
		_year = year;
		_month = month;
		_day = day;
	}

	//Date(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;
};

在这里插入图片描述

  1. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动⽣成的拷贝构造会调⽤Stack的拷贝构造,也不需要我们显示实现MyQueue的拷贝构造。这里还有⼀个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就需要显示写拷贝构造,否则就不需要。
#include<iostream>
using namespace std;

typedef int STDataType;
class Stack
{
public:
    Stack(int n = 4)
    {
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (nullptr == _a)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n;
        _top = 0;
    }
    
    //Stack(const Stack& st)
    //{
    //    // 需要对_a指向资源创建同样⼤的资源再拷⻉值


    //    _a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
    //    if (nullptr == _a)
    //    {
    //        perror("malloc申请空间失败!!!");
    //            return;
    //    }
    //    memcpy(_a, st._a, sizeof(STDataType) * st._top);
    //    _top = st._top;
    //    _capacity = st._capacity;

    //}

    void Push(STDataType x)
    {
        if (_top == _capacity)
        {
            int newcapacity = _capacity * 2;
            STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
                sizeof(STDataType));
            if (tmp == NULL)
            {
                perror("realloc fail");
                return;
            }
            _a = tmp;
            _capacity = newcapacity;
        }
        _a[_top++] = x;
    }

    ~Stack()
    {
        cout << "~Stack()" << endl;
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
 };
 
 int main()
{
    Stack st1;
    Stack st2(st1);
    return 0;
}

在这里插入图片描述
Stack不显示实现拷贝构造,用自动生成的拷贝构造完成浅拷拷贝
会导致st1和st2里面的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃。
在这里插入图片描述

  1. 传值返回会产⽣⼀个临时对象调用拷贝构造,传值调用返回,返回的是返回对象的别名(引用),没有产生拷贝。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使用引用返回是有问题的,这时的引用相当于⼀个野引用,类似⼀个野指针⼀样。传引用返回可以减少拷贝,但是⼀定要确保返回对象,在当前函数结束后还在,才能用引用返回。

在这里插入图片描述

2.运算符重载

当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。

  1. 运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。(类型 operator运算符 ()
	bool operator<(Date d1, Date d2)
	{}
	bool operator==(Date d1, Date d2)
	{}
  1. 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第二个参数。
  2. 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少⼀个。
#include<iostream>
using namespace std;

class Date
{
public:
	Date()//函数名与类名相同
	{
		cout << "这是无参数的构造函数" << endl;
		//不需要写返回值,什么都不用写
	}
	Date(int year, int month, int day)
	{
		cout << "这是带三个参数的构造函数" << endl;
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_year = 0;
		cout << "这是析构函数" << endl;
	}
    //error C2804: 二进制“operator <”的参数太多
	bool operator<(Date d1, Date d2)
	{}

private:
	int _year;
	int _month;
	int _day;
};

	//正常运行
bool operator<(Date d1, Date d2)
{}
  1. 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致
  2. 不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
//error C2018: 未知字符“0x40”
bool operator@()
{}
  1. .* \ :: \ sizeof \ ?: \ .注意以上5个运算符不能重载。
  2. 重载操作符至少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义。
//error C2803 : “operator + ”必须至少有一个类类型的形参
int operator+(int x, int y)
{
	return x + y;
}
  1. ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义(日期-日期得到天数),但是重载operator+就没有意义(日期+日期没有意义)。
  2. 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
//前置++
int operator++(Date d)
{
	return 0;
}
//后置++
int operator++(Date d,int x)
{
	return 0;
}
  1. 重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调用时就变成了对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对
    象。

运算符如果不是在类里面重载会遇到访问私有成员的问题


// 有⼏种⽅法可以解决:
 
// 1、成员放公有
  
// 2、Date提供getxxx函数
 
// 3、友元函数
 
// 4、重载为成员函数
 
bool operator==(const Date& d1, const Date& d2)
 {
 return d1._year == d2._year
 && d1._month == d2._month
 && d1._day == d2._day;
 }
 
int main()
{
	Date d1(2024, 7, 5);
	Date d2(2024, 7, 6);
	// 运算符重载函数可以显⽰调⽤
	operator==(d1, d2);
	// 编译器会转换成
	d1 == d2;
	return 0;
}

3.赋值运算符重载

赋值运算符重载是⼀个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个对象拷贝初始化给另⼀个要创建的对象

赋值运算符重载的特点

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引用,否则会传值传参会有拷贝
  2. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。(d1 = d2 = d3)
  3. 没有显式实现时,编译器会自动生成⼀个默认赋值运算符重载默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数
  4. 默认赋值重载与默认拷贝构造十分相似,像Date这样的类成员全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。想Stack这样的类,就要自己写深拷贝(默认的赋值重载会导致同一块空间连续析构两次,导致程序崩溃),这里还有⼀个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。
#include<iostream>
using namespace std;
class Date
{
public:

    Date(int year = 1, int month = 1, int day = 1)
    {
        //cout << "这是构造函数" << endl;
        _year = year;
        _month = month;
        _day = day;
    }

    Date& operator=(const Date& d)
    {
        //如果不是自己给自己赋值
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        //d1 = d2 应该返回前者(因为前者是被改变的) 所以要返回*this
        return *this;
    }

    void Print()
    {
        cout << _year << '/' << _month << '/' << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

在这里插入图片描述
请牢牢记住赋值重载是完成两个已经存在的对象进行拷贝赋值
而拷贝构造是用一个已经实例化的对象来初始化一个将要创建的对象!!!

结语

这次的分享就到这里结束了~
最后感谢您能阅读完此片文章~
如果您认为这篇文章对你有帮助的话,可以用你们的手点一个免费的赞并收藏起来哟~
如果有任何建议或纠正欢迎在评论区留言~
也可以前往我的主页看更多好文哦(点击此处跳转到主页)。

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

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

相关文章

中级职称评审到底需要准备什么材料?

职称评审需要的材料非常非常多&#xff0c;其中涉及到各类表格&#xff0c;这些小资料&#xff0c;看起来简单&#xff0c;实则做起来复杂&#xff0c;不过这种资料只能当年通知出来之后进行整理&#xff0c;今天甘建二跟大家说一下职称评审中需要提前准备的一些重要材料&#…

5.10直方图均衡化

基本概念 直方图均衡化&#xff08;Histogram Equalization&#xff09;是一种常用的图像处理技术&#xff0c;用于改善图像的对比度。通过调整图像中像素值的分布&#xff0c;直方图均衡化可以使图像的动态范围更广&#xff0c;从而增强图像的细节可见度。 直方图均衡化原理…

c++ 杂项

简答题 1、什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数是在类中定义函数时&#xff0c;在函数前加 virtual 关键字。父子类中只有一个该函数。 如果子类中没有重写该虚函数。那么父子类空间中使用的都是父类定义的该函数。 如果子类中重写了该虚函数&#xff…

【Python基础(一)】

学习分享 一、基本语法1、输出print语句2、常量的写法3、运算符 (/) 与(//)4、字符串5、列表5.1、列表查询元素是否存在5.2、列表查询元素是否存在5.3、身份运算符5.4、列表的增删改查 6、元组6.1、tuple() 7、字典8、函数8.1、值传递8.2、引用传递8.3、函数的传参 二、文件的操…

小北的JDK1.8下载、安装和环境配置教程——附件资源

​前言 亲爱的友友们&#xff0c;欢迎来到小北博客&#xff01;今天&#xff0c;我们将一起探索如何下载、安装并配置JDK 1.8&#xff0c;这是Java开发中一个非常关键的步骤。无论你是Java新手还是资深开发者&#xff0c;掌握JDK的正确安装和配置都是必不可少的。Java Download…

828华为云征文 | 基于华为云Flexus云服务器X搭建部署——AI知识库问答系统(使用1panel面板安装)

&#x1f680;对于企业来讲为什么需要华为云Flexus X来搭建自己的知识库问答系统&#xff1f;&#xff1f;&#xff1f; 【重塑知识边界&#xff0c;华为云Flexus云服务器X引领开源问答新纪元&#xff01;】 &#x1f31f; 解锁知识新动力&#xff0c;华为云Flexus云服务器X携…

【工具分享】Jigsaw勒索病毒解密工具

前言 Jigsaw勒索软件首次出现在2016年&#xff0c;以其独特的威胁手段迅速在网络安全界引起广泛关注。该恶意软件因其在勒索信中使用了恐怖电影《电锯惊魂》中的角色Billy the Puppet的图像而得名。Jigsaw不仅会加密受害者的文件&#xff0c;还会逐渐删除这些文件以迫使受害者…

基于nodejs+vue的游戏陪玩系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

大数据毕业设计选题推荐-国潮男装微博评论数据分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

当大模型成为新一代操作系统,我们如何转型AI产品经理?

大模型无疑是最近科技圈最炙手可热的时尚单品&#xff0c;跟AIGC能沾上边的工作岗位都成为行业香饽饽。许多产品经理朋友与斯年讨论如何转型AI产品经理&#xff0c;今天想通过用户体验五要素的逻辑框架&#xff0c;谈谈传统型产品经理 VS. AI型产品经理的差异。最后分享几点在转…

教师工作量数字化管理平台

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

mysql数据库的基本管理

目录 一.数据库的介绍 二.mariadb的安装 三.软件基本信息 四.数据库开启 五.数据库的安全初始化 六.数据库的基本管理 七.数据密码管理 八.用户授权 九.数据库的备份 十.web控制器 一.数据库的介绍 1.什么是数据库 数据库就是个高级的表格软件 2.常见数据库 Mysql Oracl…

Type-C接口相关知识:【总结大全】

Type-c现在非常通用了&#xff0c;所以了解Type-c也变得十分有必要了&#xff0c;还是秉承了解就要了解清楚的原则&#xff0c;我们深入的看看Type-c接口。 Type-c主要是取代上一代Micro usb接口&#xff0c;那么Type-c有什么优点呢&#xff1f; 正反可插&#xff0c;使用时不…

Vite使用vite-plugin-compression打包资源压缩

https://github.com/vbenjs/vite-plugin-compression 安装所需依赖 yarn add vite-plugin-compression -D压缩前 压缩后 使用 vite.config.ts import viteCompression from vite-plugin-compressionexport default defineConfig({plugins: [vue(),viteCompression({verbose: …

【redis-03】redis缓存穿透、缓存击穿、缓存雪崩

redis系列整体栏目 内容链接地址【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756【三】redis缓存穿透、缓存击穿、缓存雪崩htt…

如何用IDEA连接HBase

编写java代码&#xff0c;远程连接HBase进行相关的操作 一、先导依赖 代码如下&#xff1a; 二、连接成功

scroll-view滚动条在ios上没有显示滚动条,安卓上显示,亲测有效果

问题描述 微信小程序的scroll-view在ios上没有显示滚动条&#xff0c;但是如果在安卓设备上会显示一个滚动条解决方案 微信小程序只需要在scroll-view上面添加show-scrollbar“{{false}}” enhanced"{{true}}"即可解决 UniApp则修改成:show-scrollbar“false” enh…

图像背景去除的最佳工具和 PNG 网站

PNG 网站是专业人士、设计师和任何需要具有透明背景的高质量图像的人的重要资源。这些网站提供数百万个 PNG&#xff0c;这对于数字项目很有价值。更不用说&#xff0c;这些格式具有保持质量和支持透明度的能力。在这篇文章中&#xff0c;我们将探讨提供无数满足不同需求的 PNG…

产品管理 - 互联网产品(1):产品战略

1、产品方向 即产品目标、目的、方向等。根据人、公司、管理等等因素决定了产品目标有所不同&#xff0c;常见的产品目标有&#xff1a;收入、用户、市场占有率、品牌影响力、资源平衡、财务报表、抛砖引玉、融资规划等 1) 收入 从规划开始就是以赚钱为目的&#xff0c;不管…