当前位置: 首页 > news >正文

拷贝构造函数和运算符重载

文章目录

  • 拷贝构造函数
    • 特点
    • 分析拷贝构造函数情景
  • 赋值运算符重载
    • 运算符重载
      • operator<运算符重载
    • 赋值运算符
    • 前置++和后置++重载


拷贝构造函数

在创建对象的时候,是不是存在一种函数,使得能创建一个于已经存在的对象一模一样的新对象,那么接下来,让我们一起去了解一下,拷贝构造函数

特点

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

  • 拷贝构造函数是构造函数的重载
  • 拷贝构造函数的参数只有一个,且必须是类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用
  • 使用传值的方法是会报错,因为会引发无穷的递归调用
  • 若没有显示定义,编译器会生成默认的拷贝构造函数,默认的拷贝构造函数对象按照内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或值拷贝。

我们以日期类为例:

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// Date(const Date& d)  // 正确写法Date(const Date d)  // 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);return 0;
}

在这里插入图片描述

所以当拷贝构造函数的的调用要用传引用,因为传引用的话,就不需要拷贝,也就是说能调用并进入拷贝构造函数,当没有&的时候,形参进入就需要拷贝,就会调用拷贝构造函数(不进去,在形参处,就需要继续拷贝,从而无穷递归,使得编译器报错)

对于默认拷贝构造函数的使用:下面代码演示:

class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);return 0;
}

在这里插入图片描述

  1. 内置类型成员完成浅拷贝or值拷贝
  2. 自定义类型成员会调用他的拷贝构造

举例说明

//1.全是内置成员变量
class Person{public:Person(int data,int age){_data=data;_age=age;}void Print(){cout<<_data<<" "<<_age<<endl;}int _data;int _age;
};
//这个会调用默认构造函数
int main(){Person p1(10,20);Person p2(p1);    
}
//2.都是自定义函数,或者说是包括自定义函数可以使用浅拷贝,因为我们需要的就是值的传递

但是对于这样一种需要自行开辟空间如malloc的自定义类型Stack,是需要进行深拷贝的,就是需要显示拷贝构造函数

原因如下:

以Stack为例,里面malloc一个空间作为动态内存的开辟,在实例化Stack对象之后,我们会自动构造这个Stack,最后用完这个对象之后会进行销毁,~Stack()析构函数里面是由free的,如果说,我们使用默认的拷贝构造函数之后,两个对象是指向同一空间的,但是,只能free一次,所以在第二次free的时候就会报错

在这里插入图片描述

所以对于这样的拥有动态内存的自定义类型,需要我们来进行深拷贝(自定义拷贝构造函数)

总结:

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

分析拷贝构造函数情景

由下面的代码进行演示,在由拷贝构造函数的情况下的程序运行步骤

class Person{public:Person(int data){cout<<"Person构造函数"<<this<<endl;}Person(const Person& p){cout<<"拷贝构造函数"<<this<<endl;}~person(){cout<<"Person析构函数"<<this<<endl;}int data;
};Person Test(Person p){Person tmp(p);return tmp;
}
int main()
{Person p(10);Test(p);return 0;
}

在这里插入图片描述

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

赋值运算符重载

赋值运算符重载可以使得自定义类型也可以像内置类型一样进行运算符运算,以下所有的运算符重载都是以Date类为例

运算符重载

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

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

函数名为:operator+需要重载的运算符符号

注意事项:

​ 1.不能通过连接其他符号来创建新的操作符:比如operator@需要是运算符,不是任意符号
​ 2.重载操作符必须有一个类类型参数因为,我们就是对于自定义类型进行运算符运算(类似于内置类型)
​ 3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
​ 4.**.* :: sizeof ?: . ** 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

//具体的操作如下  以Date为例、
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
对于封装的问题,我们这里只能先使用public来解决,后面学到友元之后,就可以不用public
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}void Test()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << (d1 == d2) << endl;
}

我们当然可以例如上述代码一样,将operator==Date在类外面定义和声明,但是这样的话,我们如果不用友元的话,就只能将成员变量权限改为public,所以我们的方法为:将运算符重载放在Date里面

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;
};//bool operator==(const Date& d1, const Date& d2)
//{
//	return d1._year == d2._year
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}
void Test()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << d1.operator==(d2) << endl;cout << (d1 == d2) << endl;
}
int main()
{Test();return 0;
}

在这里插入图片描述

    bool operator==(const Date& d2){return _year == d2._year&& _month == d2._month&& _day == d2._day;}
//所以左操作数就是this指针,右操作为形参

operator<运算符重载

下面代码进行演示

//主要进行演示流程
class Person{public:Person(int age){_age=age;}bool operator<(const Person& p){if(_age<p.age) return true;else return false;}bool operator==(const Person& p){if(_age==p._age) return true;else return false;}bool operator<=(const Person& p){return (*this == p) && (*this < p);}int _age;
};
int main()
{Person P1(10);Person P2(20);cout << (P1 < P2) << endl;return 0;
}

为了提高服用率,我们通常将利用其他运算符重载来实现我们需要的运算符重载

    bool operator<(const Person& p){if(_age<p.age) return true;else return false;} 
//只需要知道一个 < 一个 ==就能得到所有的比较大小的运算符重载bool operator==(const Person& p){if(_age==p._age) return true;else return false;}bool operator<=(const Person& p){return (*this == p) && (*this < p);}
//我们一定注意的是,运算符重载使用的的传引用,这样可以提高效率

赋值运算符

赋值运算符为operator=,这个运算符重载有一些细节要求

赋值运算符重载的格式

  1. 参数类型:const T&,传递引用可以提高传参效率,和上述保持一致
  2. 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  3. 检测是否自己给自己赋值
  4. 返回*this :要复合连续赋值的含义
//首先我们来看内置类型赋值的情况
int main()
{int n=10;n=20;int i,j,k;i=j=k=0;//连续赋值,0先给k返回的结果给了j再给了i,以至于i=j=k//所以我们需要的返回值类型应该为类类型 例如Person//又因为,传引用返回可以提高效率(少一次拷贝构造返回),所以我们选择的是类类型&  如Person&cout<<n<<endl;//输出结果为n  这个会改变n的数值return 0;
}

所以我们自定义类型的赋值运算符重载也要这样,所以我们应该这样做

class Person{public:Person(int age){_age=age;}Person& operator=(const Person& p){if(*this!=p){//如果两个对象不是同一个对象_age=p._age;//进行赋值}return *this;//这个this的生命周期不在类里面,所以是可以传引用返回}int _age;
};
int main()
{Person p1(10);Person p2(20);cout<<p1=p2<<endl;return 0;
}

在这里插入图片描述

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

在这里插入图片描述

Vs2022版本的编译器对于全局的赋值运算符重载会直接编译报错,并提示,operator=必须是成员函数

这是因为,我们规定返回类型为传引用,但是如果是全局函数,那么左值的生命周期在调用完函数之后把就结束的,所以这个地方也有问题,且“operator =”必须是非静态成员

原因解释:

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

也就是说,这个实际上就是存在默认的赋值运算符重载,所以才不能用于全局函数

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

总结:

  • 对于赋值运算符重载,在没有自定义的时候,编译器会自动提供默认赋值运算符重载,且这个默认函数是值拷贝,也就是浅拷贝的
  • 内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
  • 对于类似于栈Stack这样的类,是需要自定义的,但是对于Date或者上述Person类是不需要的,使用默认的即可
  • 如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

前置++和后置++重载

我们先看内置类型前置++和后置++的区别

int main()
{int n=10;int a=n++;//后++,这一行n还是为10 这个代码结束 后 n为11int b=++n;//先++,这一行n就为11cout<<a<<" "<<b<<endl;return 0;
}

所以自定义类型也需要按照内置类型的方式来实现前置后置++

class Person {
public:void Print() {cout << _age << endl;}Person(int age) {_age = age;}//我们规定前置++为无参Person& operator++() {(*this)._age++;return *this;}//后置++  括号使用这个无参类型表示Person operator++(int) {Person tmp = *this;(*this)._age+=1;return tmp;//返回临时变量,所以不能传引用返回}int _age;
};
int main()
{Person p(10);cout << p._age << endl;p++;p.Print();cout << p._age << endl;++p;cout << p._age << endl;return 0;
}

在这里插入图片描述

相关文章:

拷贝构造函数和运算符重载

文章目录 拷贝构造函数特点分析拷贝构造函数情景 赋值运算符重载运算符重载operator<运算符重载 赋值运算符前置和后置重载 拷贝构造函数 在创建对象的时候&#xff0c;是不是存在一种函数&#xff0c;使得能创建一个于已经存在的对象一模一样的新对象&#xff0c;那么接下…...

本周热门chatGPT之AutoGPT-AgentGPT,可以实现完全自主实现任务,附部署使用教程

AutoGPT 是一个实验性的开源应用程序&#xff0c;它由GPT-4驱动&#xff0c;但有别于ChatGPT的是&#xff0c;​ 这与ChatGPT的底层语言模型一致。 ​AutoGPT 的定位是将LLM的"思想"串联起来&#xff0c;自主地实现你设定的任何目标。 简单的说&#xff0c;你只用提出…...

Mysql 优化LEFT JOIN语句

1.首先说一下个人对LEFT JOIN 语句的看法&#xff0c;原先我是没注意到LEFT JOIN 会影响到性能的&#xff0c;因为我平时在项目开发中&#xff0c;是比较经常见到很多个关联表的语句的。 2.阿里巴巴手册说过&#xff0c;连接表的语句最好不超过3次&#xff0c;但是我碰到的项目…...

全栈成长-python学习笔记之数据类型

python数据类型 数字类型 类型类型转换整型 intint() 字符串类型转换 浮点型保留整数 int(3.14)3 int(3.94)3浮点型 floatfloat() #####字符串类型 类型类型转换字符串 strstr() 将其他数据类型转为字符串 布尔类型与空类型 布尔类型 类型类型转换布尔型 boolbool()将其他…...

面试|兴盛优选数据分析岗

1.离职原因、离职时间点 2.上一份工作所在的部门、小组、小组人员数、小组内的分工 3.个人负责的目标&#xff0c;具体是哪方面的成本 4.为了降低专员成本&#xff0c;做了哪些方面的工作 偏向于机制、分析方法、思维&#xff0c;当下主要是对于部分高收入专员收入不合理的情况…...

Redis(08)主从复制master-slave replication

文章目录 redis主从复制一. 配置文件的方式设置1. 主节点配置:2. 从节点1配置:3. 从节点2配置: 二. 命令的方式设置1. 创建服务2. 设置主从节点3. 测试 三. 从节点升级为主节点四. 查看主从关系 redis主从复制 Redis主从复制是将一个Redis实例的数据复制到多个Redis实例&#…...

被chatGPT割了一块钱韭菜

大家好&#xff0c;才是真的好。 chatGPT热度一直上升&#xff0c;让我萌生了一个胆大而创新的想法&#xff0c; 把chatGPT嵌入到Notes客户机中来玩。 考虑到我已经下载了一个chatGPT的Notes应用&#xff08;请见《ChatGPT APIs for HCL DOMINO》&#xff09;&#xff0c;想着…...

vue3+ts+pinia+vite一次性全搞懂

vue3tspiniavite项目 一&#xff1a;新建一个vue3ts的项目二&#xff1a;安装一些依赖三&#xff1a;pinia介绍、安装、使用介绍pinia页面使用pinia修改pinia中的值 四&#xff1a;typescript的使用类型初识枚举 一&#xff1a;新建一个vue3ts的项目 前提是所处vue环境为vue3&…...

Apache安装与基本配置

1. 下载apache 地址&#xff1a;www.apache.org/download.cgi&#xff0c;选择“files for microsoft windows”→点击”ApacheHaus”→点击”Apache2.4 VC17”&#xff0c;选择x64/x86&#xff0c;点击右边download下面的图标。 2. 安装apache &#xff08;1&#xff09;把…...

哈夫曼树【北邮机试】

一、哈夫曼树 机试考察的最多的就是WPL&#xff0c;是围绕其变式展开考察。 哈夫曼树的构建是不断选取集合中最小的两个根节点进行合并&#xff0c;而且在合并过程中排序也会发生变化&#xff0c;因此最好使用优先队列来维护单调性&#xff0c;方便排序和合并。 核心代码如下…...

thinkphp:数值(保留小数点后N位,四舍五入,左侧补零,格式化货币,取整,生成随机数,数字与字母进行转换)

一、保留小数点后N位/类似四舍五入&#xff08;以保留小数点后三位为准&#xff09; number_format()函数&#xff1a;第一个参数为要格式化的数字&#xff0c;第二个参数为保留的小数位数 方法一&#xff1a; public function test() {$num 12.56789; // 待格式化的数字$r…...

用Flutter你得了解的七个问题

Flutter是Google推出的一款用于构建高性能、高保真度移动应用程序、Web和桌面应用程序的开源UI工具包。Flutter使用自己的渲染引擎绘制UI&#xff0c;为用户提供更快的性能和更好的体验。 Flutter使用Dart语言&#xff0c;具有强大的类型、效率和易学能力&#xff0c;基本上你…...

Nmap使用手册

Nmap语法 -A 全面扫描/综合扫描 nmap-A 127.0.0.1 扫描指定网段 nmap 127.0.0.1 nmap 127.0.0.1/24Nmap 主机发现 -sP ping扫描 nmap -sP 127.0.0.1-P0 无ping扫描备注&#xff1a;【协议1,协设2〕【目标】扫描 nmap -P0 127.0.0.1如果想知道是如何判断目标主机是否存在可…...

基于ResNet-attention的负荷预测

一、attention机制 注意力模型最近几年在深度学习各个领域被广泛使用&#xff0c;无论是图像处理、语音识别还是自然语言处理的各种不同类型的任务中&#xff0c;都很容易遇到注意力模型的身影。从注意力模型的命名方式看&#xff0c;很明显其借鉴了人类的注意力机制。我们来看…...

华为校招机试 - 批量初始化次数(20230426)

题目描述 某部门在开发一个代码分析工具,需要分析模块之间的依赖关系,用来确定模块的初始化顺序是否有循环依赖等问题。 "批量初始化”是指一次可以初始化一个或多个模块。 例如模块1依赖模块2,模块3也依赖模块2,但模块1和3没有依赖关系,则必须先"批量初始化”…...

WhatsApp CRM:通过 CRM WhatsApp 集成向客户发送消息

WhatsApp CRM&#xff1a;通过 CRM WhatsApp 集成向客户发送消息 你是否在寻找一个支持WhatsApp整合的CRM&#xff1f;或者&#xff0c;你想将WhatsApp与你当前的CRM整合&#xff1f;这篇文章将回答你所有的问题。我们将首先了解什么是WhatsApp CRM&#xff0c;以及你需要知道…...

SOLIDWORKS Electrical无缝集成电气和机械设计

集成电气系统设计SOLIDWORKS⑧Electrical 解决方案借助专为工程专业设计的特定工具简化了电气铲品设计&#xff0c;并借助直观的用户界面更快地设计嵌入式电气系统。 与SOLIDWORKS 3DCAD的原生集成能提供更好的协作与生产效率&#xff0c;同时减少产品延迟、提高设计的一致性与…...

Numpy从入门到精通——数组变形|合并数组

这个专栏名为《Numpy从入门到精通》&#xff0c;顾名思义&#xff0c;是记录自己学习numpy的学习过程&#xff0c;也方便自己之后复盘&#xff01;为深度学习的进一步学习奠定基础&#xff01;希望能给大家带来帮助&#xff0c;爱睡觉的咋祝您生活愉快&#xff01; 这一篇介绍《…...

DJ4-5 路由算法:LS 和 DV

目录 一、迪杰斯特拉算法 1. 术语定义 2. 算法描述 3. 举例说明 4. 构建从源节点到目的节点的路径 5. 构建最低费用路径树 6. 构建转发表 二、距离向量路由算法 1. 术语定义 2. 举例说明 3. 距离向量表 4. 更新距离向量表 5. 举例说明 三、距离向量路由算法 PLUS…...

python图像处理之形态学梯度、礼帽、黑帽

文章目录 简介实战 简介 腐蚀和膨胀是图像形态学处理的基本运算&#xff0c;这两种运算的复合运算构成了开和闭&#xff0c;而腐蚀、膨胀与原图之间的加减操作&#xff0c;则构成了形态学梯度、礼帽和黑帽计算。 由于这几种函数均基于腐蚀和膨胀&#xff0c;所以其参数均与开…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...