C++类和对象 - 拷贝构造, 赋值重载

拷贝构造函数

拷贝构造作用:一个已经存在的对象去初始化另一个要创建的对象

日常写代码中会出现如下场景: 

class Data
{
public:Data(int year, int month, int day)    // 拷贝构造函数{this->_year = year;this->_month = month;this->_day = day;}
private:int _year;int _month;int _day;
}int main()
{Data d1(2024.11.11);Data d2(d1);
}

 在上面的代码中, 我们想要使用一个已存在的对象去初始化另一个对象, 此时拷贝构造的作用就体现出来了.

class Data
{
public:Data(Data& data)    // 拷贝构造函数{this->_year = data._year;this->_month = data._month;this->_day = data._day;}
private:int _year;int _month;int _day;
}

拷贝构造函数特性:

  1. 是构造函数的一种重载形式.
  2. 参数只有一个,且必须是同类型对象的引用.

 推荐拷贝构造函数加上 const 修饰

class Data
{
public:Data(const Data& data)    // 拷贝构造函数{this->_year = data._year;this->_month = data._month;this->_day = data._day;}
private:int _year;int _month;int _day;
}

因为我们只是想要复制对象, 并不需要修改, 所以加上 const 修饰更加安全, 防止对象数据被修改.

参数必须是引用

上面说, 因为拷贝构造是不会修改对象属性的, 所以推荐加上 const 修改.

那如果我不传引用, 参数就是对象的一个副本, 这样即使修改了参数, 也不会影响外部的对象. 这样就可以不写 const.

class Data
{
public:Data(Data data)    // 拷贝构造函数{this->_year = data._year;this->_month = data._month;this->_day = data._day;}
private:int _year;int _month;int _day;
}

上面的代码是错误的. 拷贝构造函数的参数有且只有一个, 必须是同类型对象的引用.

首先, 我们知道拷贝构造函数的作用: 一个已经存在的对象去初始化另一个要创建的对象.
如果, 传参用的不是引用, 那么那个参数就是原对象的副本, 这个副本是怎么来的?
这个副本也是通过那个"已存在的对象创建出来的" , 这不就是拷贝构造函数的作用吗?
这个副本是通过拷贝构造函数创建出来的, 而拷贝构造函数又需要这个副本, 就形成了一个死循环.

这样最终就会造成无限死循环.

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

拷贝构造也是C++中的默认成员函数.
如果我们没有显示的写构造函数, 编译器就会帮我们生成一个默认的拷贝构造函数.

编译器默认生成的拷贝构造内置类型会赋值(值拷贝),自定义类型会去调用自定义类的拷贝构造.

在涉及到动态内存管理时, 使用默认生成的拷贝构造函数可能会出现问题. (浅拷贝问题)

class Stack
{
public:Stack(int size){_size = size;_array = (int*)malloc(sizeof(int) * size);if(_array == nullptr){perror("malloc 申请空间失败");}}~Stack(){if(_array){free(_array);}}
private:int* _array;int _size;
}int main()
{Stack s1(10);Stack s2(s1);
}

上面的代码中没有显示的写构造函数, 用的就是编译器生成的默认函数.

指针也属于C++的内置类型 (内置类型值拷贝), 所以通过 s1  去初始化 s2 时, s2 中的 _array 与 s1的 _array 指向的是同一块空间. 

\

可能会导致的问题:

当 s1 和 s2 生命周期结束时

它们都会调用析构函数释放空间

s1 的 _array 和 s2 的 _array 所指向的空间是同一个

s1 和 s2 一共两次析构, 就会对那块空间进行两次释放

对一快空间进行两次释放, 会导致程序出错

所以当涉及到动态内存管理时, 需要格外注意浅拷贝问题.

赋值重载

复制重载是运算符重载的一种.

复制重载就是对 "=" 实现运算符重载.

运算符重载

Data d1(2024.11.11);
Data d2 = d1 + 100;

在代码中有一个 d1 对象, 我们想得到一个新的日期类 d2, d2 日期类 = d1日期 加上100天.
我们直接使用 "+" 运算符是无法进行计算的. 

C++中, 普通的 "+" 运算符只能用于内置类型,
对于自定义类型是不适用的.
如果我们像是让 "+" 运算符支持自定义类型,
我们就需要去实现 "+" 运算符的重载.

为什么会出现运算符重载?

我们完全可以实现一个成员函数来完成这些功能

比如 "+" 运算符的功能, 我实现一个plus(int day) 成员函数

也是完全可以实现所需功能的.

使用运算符重载最主要好处是: 

便于代码的理解, 当我们在代码中看到一个成员函数

我们需要去看函数接口的文档/讲解, 而在代码中使用 "+"运算符

很容易就让人理解这是在完成 "数据相加" 的操作, + 的原始作用就是 "数据相加"

 所以使用运算符重载最重要的目的之一: 便于代码的理解.

// 返回值类型 operator运算符(参数列表)
Data operator+(Data d1, int x)
{}

 这样就完成了 "+" 运算符的重载.
返回一个 Data 类型的数据, 需要两个参数, 一个 Data 类型, 一个 int 类型数据. {} 中就是函数体

运算符重载都是针对自定义类型的
所以运算符重载一般都写在类内, 当作一个成员函数

class Data
{
public:Data(int year, int month, int day){_year = year;_month = month;_day = day;}bool operator==(const Data& d){return _year == d.year && _month == d.month && _day == d.day;}
private:int _year;int _month;int _day;
}int main()
{Data d1(2024, 11, 11);Data d2(2024, 11, 11);if(d1 == d2)    // 调用了 d1的operator==, 所以隐藏的this指向的就是d1{cout << "两个日期相等" << endl;}
}

写在内类, 第一个类型参数就不用我们自己写了.
因为成员函数默认会又一个 this 指针. 代替了我们手动传递.

operator= 赋值重载

赋值重载也是C++的默认成员函数之一.
当我们不显示的写时, 编译器会自动生成一个.
自动生成的赋值运算符重载, 对于内置类型时直接复制,
对于自定义类型则会调用对应类型的赋值运算符重载函数.

赋值运算符的重载只能写在内类

如果显示的写在了类外

那么类内和类外的两个赋值重载就会冲突

class Data
{
public:Data(int year, int month, int day){_year = year;_month = month;_day = day;}bool operator==(const Data& d){return _year == d.year && _month == d.month && _day == d.day;}Data& operator=(const Data& d)    // 赋值重载{if(*this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
}int main()
{Data d1(2024, 11, 11);Data d2 = d1;    // "=" 运算符重载if(d1 == d2)    // "==" 运算符重载{cout << "两个日期相等" << endl;}return 0;
}

this 是调用的对象的地址, 返回 *this 就是返回对象本身.

前置++和后置++

前置++ 和 后置++的参数相同, 返回值不同.
这是构不成重载的, 所以C++为了区分前置++ 和 后置++做了点特殊处理.

前置++:

Data& operator++();

后置++:

Data& operator++(int)

可以看到前置++没有什么改变.
在后置++ 中增加了一个参数.

后置++中的那个参数完全没有用处
只是为了区别前置++和后置++

 

class Data
{
public:Data& operator++()    // 前置++{}Data& operator++(int)    // 后置++{}
private:int _year;int _month;int _day;
}int main()
{Data d1(2024, 11, 11);d1++;++d1;return 0;
}

可以看到后置++多的那个参数不用关心, 只是为了区分前置++和后置++, 也无需为那个参数传参

五个不能重载的运算符

  1. "::"
  2. "." 
  3. "?:"
  4. ".*"
  5. "sizeof"

这五个运算符是不能被重载的.

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

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

相关文章

为什么咨询公司需要项目管理软件:7大关键优势

在咨询公司中&#xff0c;同时管理多个项目、客户和目标并非易事&#xff0c;尤其需要兼顾长期发展。项目管理软件就成为了各类项目型企业&#xff08;包括咨询公司&#xff09;的重要工具。 对于顾问来说&#xff0c;项目管理软件可以简化工作流程、增强客户关系并提高效率。本…

3大核心技术,免费开源的智能合同审查分析软件的技术介绍

本智能合同审查分析系统致力于解决法律领域中复杂文档与信息处理的难题&#xff0c;采用最先进的深度学习与自然语言处理&#xff08;NLP&#xff09;技术&#xff0c;提供精准的实体识别与关系抽取功能。系统基于BERT、GPT等主流模型&#xff0c;实现自动识别和关联法律文档中…

GitCode光引计划有奖征文大赛

一、活动介绍 GitCode平台汇聚了众多杰出的G-Star项目&#xff0c;它们犹如璀璨星辰&#xff0c;用各自的故事和成就&#xff0c;为后来者照亮前行的道路。我们诚邀广大开发者、项目维护者及爱好者&#xff0c;共同撰写并分享项目在GitCode平台上托管的体验&#xff0c;挖掘平…

UE4 Cook 从UAT传递参数给UE4Editor

需求 一句Cook的命令如下&#xff1a; ${EnginePath}/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun -project${ClientPath}/${ProjectName}.uproject -noP4 -platformIOS -cooksinglepackage -client -clientconfig${CookConfig} -iterate -skipbuild -nocompile -NoMutex…

api驱动的云服务是什么意思?

API驱动的云服务是指利用API技术来驱动和提供云服务的模式。在这种模式下&#xff0c;云服务提供商会公开一系列的API接口&#xff0c;允许开发者或应用程序通过调用这些API来实现对云服务的访问和操作。API驱动的云服务是现代云计算技术的重要组成部分&#xff0c;API驱动的云…

Springboot 启动端口占用如何解决

Springboot 启动端口占用如何解决 1、报错信息如下 *************************** APPLICATION FAILED TO START ***************************Description:Web server failed to start. Port 9010 was already in use.Action:Identify and stop the process thats listening o…

【Rust调用Windows API】杀掉指定进程(包括兄弟进程、子进程、父进程)

前言 前面一篇文章写了使用Rust调用Windows API 获取正在运行的全部进程信息 &#xff0c;本篇实现杀掉指定进程。 通过标准库可以管理当前进程创建的子进程&#xff0c;要 kill 掉子进程也比较容易&#xff0c;这里不赘述了&#xff0c;主要实现通过调用Windows API来杀掉兄…

基于Python的外卖点餐系统

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

世界坐标系、相机坐标系、图像物理坐标系、像素平面坐标系

坐标系及其转换在计算机视觉领域占据核心地位。理解如何从一个坐标系转换到另一个坐标系&#xff0c;不仅是理论上的需要&#xff0c;也是实际应用中不可或缺的技能。 一、世界坐标系的定义 世界坐标系是一个全局的坐标系统&#xff0c;用于定义场景中物体的位置。在这个坐标…

Pycharm也可以用来查看和操作MySQL数据库?

1.首先确保自己的电脑安装好MySQL MySQL :: Download MySQL Community Server 2.在创建project界面选择Open 打开并选择自己存放sql文件的路径 完成后的效果 3.创建好数据库的连接 4.打开Database视窗 5.给数据库添加新的Schema 6.运行需要查看或者操作的sql文件 7.选择适合的…

1、VMware12安装ubuntu18.04

1、ubuntu18地址获取&#xff1a; Index of /ubuntu-releases/18.04/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 2、安装过程&#xff1a; 1、新建虚拟机 2、选择稍后安装操作系统 3.客户机操作系统选择Linux&#xff0c;版本选择Ubuntu 64位 4、点击“自定义…

数据结构查找-哈希表(创建+查找+删除)+(C语言代码)

#include<stdlib.h> #include<stdio.h> #include<stdbool.h> #define NULLKEY -1//单元为空 #define DELKEY -2//单元内容被删除 #define M 20 typedef struct {int key;//关键字int count;//统计哈希冲突探测次数 }HashTable; //插入到哈希表 void InsertHT…

基于Java的周次生成工具类实现

摘要&#xff1a; 本文介绍了一个基于Java的周次生成工具&#xff0c;用于根据指定的年份和周次类型&#xff08;自然周或财务周&#xff09;生成连续的周次列表数据。 需求描述&#xff1a; 根据周次类型&#xff0c;生成连续的周次列表数据。 周次类型分2种&#xff1a; …

为什么华大严选基因是您的最佳选择?品牌特色全解析

《为什么华大严选基因是您的最佳选择&#xff1f;品牌特色全解析》 在当今基因检测市场竞争激烈的环境下&#xff0c;华大严选基因以其卓越的品质和独特的品牌特色脱颖而出&#xff0c;成为众多消费者的首选。作为 DNA 基因检测行业十佳优质品牌和 3・15 重点推荐品牌&#xff…

中仕公考怎么样?国考有三不限岗位吗?

国考有三不限岗位吗? “三不限”岗位&#xff0c;即不限制专业、学历和户籍的岗位。国考中几乎没有“三不限”岗位。因为公务员国考的报考条件通常较为严格&#xff0c;尤其是不限制条件的职位更少见&#xff0c;所以国考中一般是没有三不限岗位的。 与省考公务员不同的是&a…

Day44 | 动态规划 :状态机DP 买卖股票的最佳时机IV买卖股票的最佳时机III

Day44 | 动态规划 &#xff1a;状态机DP 买卖股票的最佳时机IV&&买卖股票的最佳时机III&&309.买卖股票的最佳时机含冷冻期 动态规划应该如何学习&#xff1f;-CSDN博客 本次题解参考自灵神的做法&#xff0c;大家也多多支持灵神的题解 买卖股票的最佳时机【…

inode,请别忽视它!

在Linux文件系统中&#xff0c;inode 是一个不可忽视的核心概念。它不仅是文件系统运行的基石&#xff0c;还在内核、应用程序以及驱动程序开发中扮演着重要角色。如果你曾对文件的底层运作感到困惑&#xff0c;或者在面试中遇到文件系统相关问题&#xff0c;那么理解inode绝对…

x-cmd pkg | howdoi - 无需打开浏览器,轻松搜索编程问题的解决方案

目录 简介首次用户技术特点竞品和相关项目进一步阅读 简介 howdoi 一个在终端中查找编程问题的命令行工具和 python 库&#xff0c;可用于从 Stack Overflow 等编程社区获取搜索问题的答案&#xff0c;并将它们以代码片段的形式显示在命令行中。 首次用户 本文提供了一个dem…

实测运行容器化Tomcat服务器

文章目录 前言一、拉取Tomcat 9.0镜像二、运行容器化Tomcat服务器三、访问Tomcat官网首页测试 总结 前言 运行容器化Tomcat服务器&#xff0c;首先确保正确安装docker&#xff0c;并且已启动运行&#xff0c;具体安装docker方法见笔者前面的博文《OpenEuler 下 Docker 安装、配…

Enhanced Table如何安装?

githup项目地址&#xff1a; https://github.com/fbaligand/kibana-enhanced-table/releases/tag/v1.12.0