拷贝构造、赋值运算符、运算符重载

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

拷贝构造函数

赋值运算符重载

运算符重载

日期类相关的所有操作符的重载

重载赋值运算符

拷贝构造和赋值运算符的显著区别


拷贝构造函数

那在创建对象时,可否创建一个与一个对象一模一样的新对象呢?

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

特征

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

2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引起无穷递归

3.若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。

class Date1
{
public:Date1(int year=0,int month=0,int day=0){_year=year;_month=month;_day=day;}
private:int _year;int _month;int _day;
};
int main()
{Date1 d1(2023,7,23);//这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的。Date1 d2(d1);return 0;
}

4. 那么编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,我们还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

class Stack
{
public:Stack(int n){_a=(int*)malloc(sizeof(int)*n);_top=0;}void STPush(int x){_a[_top]=x;_top++;}void STPop(){assert(!STEmpty());_top--;}bool STEmpty(){return _top;}int STsize(){return  _top;}void show(){int count=_top-1;while(count>=0){cout<<_a[count]<<endl;count--;}}~Stack(){free(_a);}
private:int* _a;int _top;
};
int main()
{Stack s1(10);Stack s2(s1);return 0;
}
结果:
7_21(2689,0x1000ebe00) malloc: *** error for object 0x100643370: pointer being freed was not allocated
7_21(2689,0x1000ebe00) malloc: *** set a breakpoint in malloc_error_break to debug

这里就会有问题了,因为我们没有显示定义拷贝构造函数,所以编译器使用的默认浅拷贝,对象中s1中的_a数组是把首地址拷贝给s2,析构的时候,先析构s2,在析构s1,问题出现在这,_a这一个空间,释放了两次,所以编译器就会报错。

我们这里就需要自己先定义一个,拷贝构造函数,实现深拷贝

//拷贝构造函数
//深拷贝
Stack(const Stack& st)
{_a=(int*)malloc(sizeof(int)*st._capacity);memcpy(_a, st._a, sizeof(int)*st._top);_top=st._top;_capacity=st._capacity;
}

赋值运算符重载

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

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

函数原型:返回值类型 operator操作符(参数列表)

注意:

1.不能通过连接其他符号来创建新的操作符:比如operator@(必须是已经存在的操作符)

2.重载操作符必须有一个类类型或者枚举类型的操作数

3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义

4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参

5. .* ,:: ,sizeof ,?: ,. 注意以上5个运算符不能重载

class Date1
{
public:Date1(int year=0,int month=0,int day=0){_year=year;_month=month;_day=day;}//bool operator==(Date1* this,const Date1& di)//这里需要注意的是,左操作数是this指向的调用函数的对象bool operator==(const Date1& di){return _year==di._year&&_month==di._month&&_day==di._day;}
private:int _year;int _month;int _day;
};
日期类相关的所有操作符的重载
class Date
{
public:Date(int year=0,int month=0,int day=0);Date(const Date& d);// <<friend ostream& operator<<(ostream& out,Date d);//赋值运算符的重载Date& operator=(const Date& d);int GetMonthDay(int year,int month);//日期+=天数Date& operator+=(int day);//日期+天数Date operator+(int day);// 日期-=天数Date& operator-=(int day);// 日期-天数Date operator-(int day);//前置++Date& operator++();//后置++Date operator++(int);//前置--Date& operator--();//后置--Date operator--(int);//==bool operator==(const Date& d);//!=bool operator!=(const Date& d);//>=bool operator>=(const Date& d);//>bool operator>(const Date& d);// <=bool operator<=(const Date& d);// <bool operator<(const Date& d);int GetYearDay(int year);//日期-日期 返回天数int operator-(const Date& d);void show();~Date();
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out,Date d)
{out<<d._year<<"/"<<d._month<<"/"<<d._day<<endl;return out;
}
Date::Date (int year,int month,int day)
{_year=year;_month=month;_day=day;
}
Date:: Date(const Date& d)
{_year=d._year;_month=d._month;_day=d._day;
}
Date& Date::operator=(const Date& d)
{if(this!=&d){_day=d._day;_month=d._month;_year=d._year;}return *this;
}
int Date::GetMonthDay(int year,int month)
{int a[]={0,31,28,31,30,31,30,31,31,30,31,30,31};if(month==2&&((year%4==0&&year%100!=0)||year%400==0))return a[month]+1;return a[month];
}
//日期+=天数
Date& Date::operator+=(int day)
{if(day<0)return *this-=(-day);_day+=day;while(_day>GetMonthDay(_year, _month)){_day-=GetMonthDay(_year, _month);_month++;if(_month>12){_month=1;_year++;}}return *this;
}
//日期+天数
Date Date::operator+(int day)
{Date temp(*this);temp+=day;return temp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{if(day<0)return *this+=(-day);while((_day-day)<0){_month--;if(_month<=0){_month=12;_year--;}day-=GetMonthDay(_year, _month);}_day-=day;return *this;
}
// 日期-天数
Date Date::operator-(int day)
{Date temp(*this);temp-=day;return temp;
}
//前置++
Date& Date::operator++()
{*this+=1;return *this;
}
//后置++
Date Date::operator++(int)
{Date temp(*this);temp+=1;return temp;
}
//前置--
Date& Date::operator--()
{*this-=1;return *this;
}
//后置--
Date Date::operator--(int)
{Date temp(*this);temp-=1;return temp;
}
//==
bool Date::operator==(const Date& d)
{if(_year!=d._year)return false;else{if(_month!=d._month)return false;else{if(_day!=d._day)return false;return true;}}
}
//!=
bool Date::operator!=(const Date& d)
{return !(*this==d);
}
//>=
bool Date::operator>=(const Date& d)
{return (*this>d)||(*this==d);
}
//>
bool Date::operator>(const Date& d)
{if(_year>d._year)return true;else{if(_year>d._year&&_month>d._month)return true;else{if(_year>d._year&&_month>d._month&&_day>d._day)return true;elsereturn false;}}
}
// <=
bool Date::operator<=(const Date& d)
{return (*this<d)||(*this==d);
}
// <
bool Date::operator<(const Date& d)
{if(_year<d._year)return true;else{if(_year<d._year&&_month<d._month)return true;else{if(_year<d._year&&_month<d._month&&_day<d._day)return true;elsereturn false;}}
}
int Date::GetYearDay(int year)
{if((year%4==0&&year%100!=0)||year%400==0)return 366;return 365;
}
//日期-日期 返回天数
//int Date::operator-(const Date& d)
//{
//    int day=0,month=0,year=0;
//    year=d._year;
//    month=d._month;
//    day=d._day;
//    while(_month>0)
//    {
//        _day+=GetMonthDay(_year, _month);
//        _month--;
//    }
//    while(month>0)
//    {
//        day+=GetMonthDay(year, month);
//        month--;
//    }
//    int CountY=_year-year;
//    if(CountY>0)
//    {
//        while(CountY--)
//        {
//            _day+=GetYearDay(year++);
//        }
//    }
//    return _day-day;
//}
int Date::operator-(const Date& d)
{Date min=d;Date max=*this;int flag=1;if(*this<d){min=*this;max=d;flag=-1;}int n=0;while(min!=max){++min;n++;}return n*flag;
}
void Date::show()
{cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
}
Date::~Date()
{cout<<"日期功能结束"<<endl;
}

重载赋值运算符

在定义的同时进行赋值叫做初始化,定义完成以后再赋值(不管在定义的时候有没有赋值)就叫做赋值。初始化只能有一次,赋值可以有多次。

当以拷贝的方式初始化一个对象时,会调用拷贝构造函数;当给一个对象赋值时,会调用重载过的赋值运算符。即使没有显式的重载赋值运算符,编译器也会以默认地方式重载它。默认重载的赋值运算符功能很简单,就是将原有对象的所有成员变量一一赋值给新对象,这和默认拷贝构造函数的类似。

编译器默认的赋值运算符

#include<iostream>
using namespace std;
class Date
{
public:Date(int year=0,int month=0,int day=0):_year(year),_month(month),_day(day){}void show(){cout<<_year<<"/"<<_month<<"/"<<_day<<endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023,7,27);Date d2;d2=d1;//这里我们没有显示对赋值运算符重载,这里使用的编译器中默认的赋值运算符重载函数d1.show();d2.show();return 0;
}
结果:
2023/7/27
2023/7/27

对于简单的类,默认的赋值运算符一般就够用了,我们也没有必要再显式地重载它。但是当类持有其它资源时,例如动态分配的内存、打开的文件、指向其他数据的指针、网络连接等,默认的赋值运算符就不能处理了,我们必须显式地重载它,这样才能将原有对象的所有数据都赋值给新对象。

例如:

#include<iostream>
using namespace std;
class Date
{
public:Date(int year=0,int month=0,int day=0):_year(year),_month(month),_day(day){_cal =(int*)malloc(sizeof(int)*10);}void show(){cout<<_year<<"/"<<_month<<"/"<<_day<<endl;}~Date(){free(_cal);}
private:int* _cal;int _year;int _month;int _day;
};
int main()
{Date d1(2023,7,27);Date d2;d2=d1;d1.show();d2.show();return 0;
}
结果:
2023/7/27
2023/7/27
8_9(2462,0x1000efe00) malloc: *** error for object 0x101b2ad70: pointer being freed was not allocated
8_9(2462,0x1000efe00) malloc: *** set a breakpoint in malloc_error_break to debug

这里为什么会运行崩溃呢?Date类里面成员变量_cal,cal指向了一块动态开辟的空间,而动态开辟的空间需要我们手动释放,因此我们在析构函数中释放_cal动态开辟的空间。我们把d1赋值给d2时,其中是将d2._cal指向d1._cal指向的空间,所以赋值完成后,d1._cal和d2._cal指向同一块空间,程序结束时会调用析构函数,编译器就会先调用d2的析构函数,对d2._cal指向的空间进行释放,然后编译器再调用d2的析构函数,对d1._cal指向的空间进行释放,因为d1._cal和d2._cal指向同一块空间,这块空间已经被释放,free再次释放就会失败,所以就会运行崩溃。一句话来说就是对一块空间重复释放。这就是默认的赋值运算符实现的浅拷贝带来的问题。

怎么样上面的问题?我们可以显示定义赋值运算符,实现深拷贝的功能

Date& operator=(const Date& d)
{_cal=(int*)malloc(sizeof(int)*10);memcpy(_cal, d._cal, 40);_year=d._year;_month=d._month;_day=d._day;return *this;
}

赋值运算符主要有四点:

1. 参数类型

2. 返回值

3. 检测是否自己给自己赋值

4. 返回*this

5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝(浅拷贝)。

拷贝构造和赋值运算符的显著区别

拷贝构造:一个对象已经创建,一个还没有

例如:Date d1(d2)    Date d1=d2

赋值重载:两个对象都存在

例如:d1=d2

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

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

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

相关文章

Electron 报错:WinState is not a constructor

文章目录 问题分析 问题 在使用 electron-win-state 库时报错如下 代码如下&#xff1a; const WinState require(electron-win-state) const winState new WinState({ defaultWidth: 800,defaultHeight: 600,// other winState options, see below })const browserWindow…

京东手势验证码-YOLO姿态识别+Bézier curve轨迹拟合

这次给老铁们带来的是京东手势验证码的识别。 目标网站&#xff1a;https://plogin.m.jd.com/mreg/index 验证码如下图: 当第一眼看到这个验证码的时候&#xff0c;就头大了&#xff0c;这玩意咋识别&#xff1f;&#xff1f;&#xff1f; 静下心来细想后的一个方案&#xf…

原来pip是有默认路径的。

今天一直报错&#xff1a; bash: /root/data1/anaconda3/envs/li_3_10/bin/pip: /root/lsc/anaconda3/envs/li_3_10/bin/python: bad interpreter: No such file or directory 原来是root/data1/anaconda3/envs/li_3_10/bin/pip: 这个位置的pip 自身带默认路径&#xff0c;然…

Python 正则表达式(一)

文章目录 概念正则函数match函数正则表达式修饰符意义&#xff1a; 常用匹配符限定符原生字符串边界字符 概念 正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成一个“规则字符串”&#xff0c;这个…

【问题解决】本地pnpm版本与packageManager中pnpm版本不一致

问题&#xff1a;ERR_PNPM_BAD_PM_VERSION  This project is configured to use v8.6.10 of pnpm. Your current pnpm is v9.1.0 解决&#xff1a;If you want to bypass this version check, you can set the “package-manager-strict” configuration to “false” or set…

Navicat 17 的数据分析

上周的博客预告了 Navicat 17&#xff08;英文版&#xff09;即将发布&#xff0c;目前正在测试阶段&#xff0c;并计划于 5 月 13 日发布。如我们所见&#xff0c;版本 17 推出了众多令人兴奋的新功能。其中最大亮点是数据分析工具&#xff0c;只需点击按钮&#xff0c;即可为…

万字长文——前端开发必看的KeepAlive原理详解

前言 本文将从原理应用源码(Vue2和Vue3)的角度全面介绍 组件&#xff0c;全文共计16000字&#xff0c;阅读时间大概30min&#xff0c;建议码住在看&#xff0c;相信看完本文的你会对该组件有一更深刻的认识。 一、<KeepAlive>是什么&#xff1f; <KeepAlive>是一个…

【数据结构】单链表和双链表

文章目录 一、链表的概念及结构二、链表的分类三、无头单向非循环链表1.单链表创建2.尾插和头插3.尾删和头删4.打印5.查找6.插入7.删除8.销毁 四、带头双向循环链表1.双链表的创建2.初始化3.判断链表是否为空4.尾插和头插5.尾删和头删6.查找7.插入8.删除9.销毁 五、总结链表和顺…

[力扣题解]93. 复原 IP 地址

题目&#xff1a;93. 复原 IP 地址 思路 回溯法&#xff1b; 特别的是&#xff0c;用pointNum来记录.的数量&#xff0c;并且没有创建path&#xff0c;而是直接在原来的strings中插入.&#xff1b; 同时&#xff0c;在判断子串合法性的时候&#xff0c;0是合法的&#xff0c;…

Java中使用alibaba的easyexcel中的方法实现csv模板下载功能

系列文章目录 文章目录 系列文章目录一、EasyExcelUtil工具 一、EasyExcelUtil工具 /*** param response 响应* param fileName 文件名称* param sheetName sheet名称* param headNameList 头部名称* param <T>* throws IOException*/public static <T>…

基于Springboot+Vue的Java项目-车辆管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

Spring自动装配:解析原理与实践

在Spring框架中&#xff0c;自动装配是一种强大的特性&#xff0c;它能够根据一定的规则自动地将bean装配到Spring容器中&#xff0c;从而简化了配置和开发过程。本文将深入探讨Spring自动装配的原理和实践&#xff0c;帮助程序员更好地理解和应用这一重要特性。 1. 什么是自动…

35个矩阵账号,如何通过小魔推打造2704万+视频曝光?

在如今的短视频时代&#xff0c;矩阵发布的作用被发挥到极致&#xff0c;通过各个短视频平台的流量分发&#xff0c;虽然视频质量不如那些头部的IP&#xff0c;但是在视频数量上却能做到轻松碾压&#xff0c;让自己的品牌与门店有更多的声量&#xff0c;这就是如今短视频平台对…

安卓实现视频录制与显示和翻转摄像头

权限&#xff1a; <!-- 相机权限 --> <uses-featureandroid:name"android.hardware.camera"android:required"false" /> <uses-permission android:name"android.permission.CAMERA" /><!-- 录音权限&#xff08;包括麦克…

2024好用的网页客服系统推荐?

2024好用的网页客服系统推荐&#xff1f;Zoho SalesIQ是一款强大的实时聊天工具&#xff0c;专为网站和在线商店设计。它提供了一套全面的功能&#xff0c;帮助企业实时解决客户问题&#xff0c;提高转化率和客户满意度。 实时监控 Zoho SalesIQ能够实时监控网站的访问者活动&…

能源系统升级BACnet IP分布式I/O边缘模块深度整合

能源管理系统(EMS)的高效运行成为了实现绿色建筑、节能减排的关键。而BACnet IP分布式远程I/O模块作为这一系统中的重要组件&#xff0c;正发挥着不可小觑的作用。本文将以某大型商业综合体为例&#xff0c;探讨BACnet IP I/O模块如何在能源管理中大显身手。 商业综合体涵盖办公…

波分系统中的EDFA光纤放大器

功能&#xff1a; 实现C波段光信号整体放大总波长范围覆盖1528~1565nm支持系统实现不同跨段的无电中继传亮点&#xff1a; 宽增益范围&#xff1a;1528nm~1565nm三种光放大器C波段应用&#xff1a; BA功率放大器LA线路放大器PA前置放大器 低噪声系数&#xff0c;典型值&#xf…

一文教你在windows上实现ollama+open webui、外网访问本地模型、ollama使用GPU加速

前言&#xff1a; ollama工具的出现让大语言模型的部署变得格外的轻松&#xff0c;但是在windows系统部署之后发现无法使用GPU进行加速&#xff0c;通过多方面查找资料发现可以在docker中使用命令启用GPU加速。另外通过Docker也可以快速部署open webui,于是本文基于docker实现…

cmake进阶:文件操作之写文件

一. 简介 cmake 提供了 file() 命令可对文件进行一系列操作&#xff0c;譬如读写文件、删除文件、文件重命名、拷贝文件、创建目录等等。 接下来 学习这个功能强大的 file() 命令。 本文学习 CMakeLists.txt语法中写文件操作。 二. cmake进阶&#xff1a;文件操作之写文件…

【活动】如何通过AI技术提升内容生产的效率与质量

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 如何通过AI技术提升内容生产的效率与质量引言一、自然语言处理&#xff08;NLP&…