深入剖析 C++ 默认函数:拷贝构造与赋值运算符重载

目录

1. 简单认识C++ 类的默认函数

1.1 默认构造函数

1.2 析构函数

1.3 拷贝构造函数

2. 拷贝构造函数的深入理解

拷贝构造的特点: 

实际运用

3. 赋值运算符重载的深入理解

3.1.运算符重载

3.2样例

1.比较运算符重载

2.算术运算符重载

3.自增和自减运算符重载

4.输入输出运算符重载

 3.3.赋值运算符重载

总结


在 C++ 编程中,类的默认函数拷贝构造函数以及赋值运算符重载是非常重要的概念。它们不仅可以让我们更方便地操作对象,还能增强代码的可读性和可维护性。本文将结合提供的日期类(Date)代码,深入剖析这些概念。

1. 简单认识C++ 类的默认函数

在 C++ 中,当我们定义一个类时,编译器会自动为我们生成一些默认的成员函数,包括默认构造函数、析构函数、拷贝构造函数和赋值运算符重载函数。不过,当我们手动定义了其中某些函数时,编译器就不会再生成对应的默认函数了。

1.1 默认构造函数

默认构造函数是在创建对象时没有提供任何参数时调用的函数。在Date类中,我们手动定义了一个带参数的构造函数:

Date(int year = 1, int month = 1, int day = 1)
{_year = year;_month = month;_day = day;
}

这个构造函数有默认参数,因此它既可以作为带参数的构造函数使用,也可以作为默认构造函数使用。当我们创建对象时不提供参数,就会使用默认值year = 1, month = 1, day = 1

1.2 析构函数

析构函数在对象生命周期结束时自动调用,用于释放对象占用的资源。在Date类中,析构函数如下:

~Date()//可不写
{_year = 0;_month = 0;_day = 0;
}

1.3 拷贝构造函数

拷贝构造函数用于创建一个新对象,该对象是另一个已存在对象的副本。在Date类中,注释掉的拷贝构造函数如下:

// Date(const Date& d1)//可不写
// {
//     _year = d1._year;
//     _month = d1._month;
//     _day = d1._day;
// }

2. 拷贝构造函数的深入理解

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


拷贝构造的特点: 


1.拷贝构造函数是构造函数的一个重载。
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。
3.C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。
4.若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。

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

实际运用

拷贝构造函数的原型通常为ClassName(const ClassName& other)。当我们使用以下方式创建对象时,会调用拷贝构造函数:

Date d1(2025, 6, 12);
Date d2(d1); // 调用拷贝构造函数

Date类中,由于成员变量都是基本数据类型,浅拷贝就足够了。但如果类中包含动态分配的资源(如指针),浅拷贝可能会导致多个对象指向同一块内存,从而在析构时引发重复释放的问题,这时就需要手动定义拷贝构造函数进行深拷贝。

3. 赋值运算符重载的深入理解

3.1.运算符重载

1.当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
2.运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体。
3.重载运算符函数的参数个数和该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。
4.如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个。
5.运算符重载以后,其优先级和结合性与对应的内置类型运算符保持一致。不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
6.注意以下5个运算符不能重载

7.重载操作符至少有一个类类型参数,不能通过运算符重载改变内置类型对象的含义,如:int
operator+(int x,int y)
8.一个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,但是重载operator+就没有意义。

3.2样例

1.比较运算符重载

比较运算符(==!=>>=<<=)用于比较两个对象的大小关系。以==运算符重载为例:

bool Date::operator==(const Date& d) const
{return _year == d._year&& _month == d._month&& _day == d._day;
}

 这个函数比较两个Date对象的年、月、日是否相等,如果相等则返回true,否则返回false。其他比较运算符的重载也类似,通过比较成员变量的值来确定对象的大小关系。

2.算术运算符重载

算术运算符(+-+=-=)用于对日期进行加减操作。以+=运算符重载为例

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 == 13){_year++;_month = 1;}}return *this;
}

这个函数将日期加上指定的天数,并处理了跨月和跨年的情况。+运算符重载则是通过调用+=运算符重载来实现的:

Date Date::operator+(int day) const
{Date tmp = *this;tmp += day;return tmp;
}
3.自增和自减运算符重载

自增和自减运算符(++--)分为前置和后置两种形式。前置运算符返回引用,后置运算符返回临时对象。以前置++运算符重载为例:

Date& Date::operator++()
{*this += 1;return *this;
}

后置++运算符重载通过一个int参数来区分前置和后置:

Date Date::operator++(int)
{Date tmp = *this;*this += 1;return tmp;
}
4.输入输出运算符重载

输入输出运算符(<<>>)用于将对象输出到流中或从流中读取对象。

ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

 这个函数将Date对象的年、月、日输出到输出流中。>>运算符重载则用于从输入流中读取年、月、日,并检查输入的日期是否合法:

istream& operator>>(istream& in, Date& d)
{while(1){cout << "请输入年月日:>";in >> d._year >> d._month >> d._day;if(!d.CheckDate()){cout << "输入的日期非法!";d.Print();cout << "请重新输入年月日!!" << endl;}else{break;}}return in;
}

 3.3.赋值运算符重载

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

赋值运算符重载的特点:
1.赋值运算符重载是一个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引用,否则会传值传参会有拷贝
2.有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续值场景。
3.没有显式实现时,编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载行为跟默认构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。
4.像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载,也不需要我们显示实现MyQueue的赋值运算符重载。这里还有一个小技巧,如果一个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要。

总结

通过对Date类的分析,我们深入了解了 C++ 类的默认函数、拷贝构造函数和赋值运算符重载的概念和用法。默认函数为我们提供了基本的对象创建和销毁机制,拷贝构造函数用于创建对象的副本,运算符重载则让我们可以像使用内置类型一样使用自定义类型进行运算。在实际编程中,我们需要根据类的具体情况来决定是否需要手动定义这些函数,以确保代码的正确性和性能。

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

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

相关文章

板凳-------Mysql cookbook学习 (十--3)

5.16 用短语来进行fulltext查询 mysql> select count(*) from kjv where match(vtext) against(God); ---------- | count(*) | ---------- | 0 | ---------- 1 row in set (0.00 sec)mysql> select count(*) from kjv where match(vtext) against(sin); -------…

python爬虫ip封禁应对办法

目录 一、背景现象 二、准备工作 三、代码实现 一、背景现象 最近在做爬虫项目时&#xff0c;爬取的网站&#xff0c;如果发送请求太频繁的话&#xff0c;对方网站会先是响应缓慢&#xff0c;最后是封禁一段时间。一直是拒绝连接&#xff0c;导致程序无法正常预期的爬取数据…

【AIGC】Qwen3-Embedding:Embedding与Rerank模型新标杆

Qwen3-Embedding&#xff1a;Embedding与Rerank模型新标杆 一、引言二、技术架构与核心创新1. 模型结构与训练策略&#xff08;1&#xff09;多阶段训练流程&#xff08;2&#xff09;高效推理设计&#xff08;3&#xff09;多语言与长上下文支持 2. 与经典模型的性能对比 三、…

算法竞赛阶段二-数据结构(32)数据结构简单介绍

数据结构的基本概念 数据结构是计算机存储、组织数据的方式&#xff0c;旨在高效地访问和修改数据。它是算法设计的基础&#xff0c;直接影响程序的性能。数据结构可分为线性结构和非线性结构两大类。 线性数据结构 线性结构中&#xff0c;数据元素按顺序排列&#xff0c;每…

Windows桌面图标修复

新建文本文件&#xff0c;粘入以下代码&#xff0c;保存为.bat文件&#xff0c;管理员运行这个文件 duecho off taskkill /f /im explorer.exe CD /d %userprofile%\AppData\Local DEL IconCache.db /a start explorer.exe echo 执行完成上面代码作用是删除桌面图标缓存库&…

13.react与next.js的特性和原理

&#x1f7e1; 一句话总结 React 专注于构建组件&#xff0c;而 Next.js 是基于 React 的全栈框架&#xff0c;提供了页面路由、服务端渲染和全栈能力&#xff0c;让你能快速开发现代 Web 应用。 React focuses on building UI components, while Next.js is a full-stack fra…

全栈监控系统架构

全栈监控系统架构 可观测性从数据层面可分为三类&#xff1a; 指标度量(Metrics)&#xff1a;记录系统的总体运行状态。事件日志(Logs)&#xff1a;记录系统运行期间发生的离散事件。链路追踪(Tracing)&#xff1a;记录一个请求接入到结束的处理过程&#xff0c;主要用于排查…

云服务运行安全创新标杆:阿里云飞天洛神云网络子系统“齐天”再次斩获奖项

引言 为认真落实工信部《工业和信息化部办公厅关于印发信息通信网络运行安全管理年实施方案的通知》&#xff0c;2025年5月30日中国信息通信研究院于浙江杭州举办了“云服务运行安全高质量发展交流会”&#xff0c;推动正向引导&#xff0c;巩固云服务安全专项治理成果。会上&a…

刀客doc:WPP走下神坛

一、至暗时刻&#xff1f; 6月11日&#xff0c;快消巨头玛氏公司宣布其价值17 亿美元&#xff0c;在全球70个市场的广告业务交给阳狮集团&#xff0c;这其中包括M&Ms、士力架、宝路等知名品牌。 此前&#xff0c;玛氏公司一直是WPP的大客户。早在今年3月&#xff0c;WPP就…

进行性核上性麻痹饮食攻略:营养安全双护航

进行性核上性麻痹是一种罕见的神经系统退行性疾病&#xff0c;主要影响患者的运动、平衡和吞咽功能。除了医学干预&#xff0c;科学的饮食管理也能在一定程度上减轻症状&#xff0c;提高生活质量。 由于患者常出现吞咽困难&#xff0c;食物质地的选择尤为重要。应避免干硬、大块…

阿里云可观测 2025 年 5 月产品动态

本月可观测热文回顾 文章一览&#xff1a; StoreView SQL&#xff0c;让数据分析不受地域限制 不懂 PromQL&#xff1f;AI 智能体帮你玩转大规模指标数据分析 DeepWiki LoongCollector&#xff1a;AI 重塑开源代码理解 从 o11y 2.0 说起&#xff0c;大数据 Pipeline 的「…

React 基础状态管理方案

1. useState useState 是 React 提供的最基本的 Hook,用于在函数组件中添加状态管理。它返回一个状态变量和一个更新状态的函数。 1.1. 使用场景 适合管理简单的状态。 适合管理组件内部的局部状态。 1.2. 示例代码 import React, { useState } from react;function Cou…

VScode中如何创建项目分支

在 VS Code 中为前端项目创建自己的分支是一个常见的开发实践&#xff0c;以下是详细步骤&#xff1a; 前提条件 已安装 Git已安装 VS Code已有前端项目或克隆了远程仓库 创建分支步骤 1. 打开项目 在 VS Code 中打开你的前端项目文件夹。 2. 初始化 Git 仓库&#xff08…

Flutter 导航与路由管理:Navigator 的深入解析与实践

在移动应用开发中&#xff0c;页面导航是用户体验的核心组成部分。Flutter 提供了强大而灵活的导航系统&#xff0c;主要通过 Navigator 组件来实现。本文将全面介绍 Flutter 中 Navigator 的使用方法&#xff0c;涵盖基础导航操作、进阶技巧以及最佳实践。 一、Flutter 导航系…

预测性去匿名化攻击(PDAA):重塑数据安全攻防边界

一、数据合规时代的“脱敏”悖论&#xff1a;价值释放与风险并存 在数据驱动的商业模式与日趋严格的个人信息保护法规&#xff08;如《个人信息保护法》《数据安全法》&#xff09;双重推动下&#xff0c;企业普遍将“数据脱敏”作为实现数据合规与价值释放的核心手段。对手机…

[python] 使用python设计滤波器

使用python设计滤波器 文章目录 使用python设计滤波器完整滤波器设计代码&#xff08;未经完整验证&#xff0c;博主还在不断完善中&#xff09;关键原理与代码对应说明1. 滤波器类型选择2. 阶数估算原理3. 性能分析技术4. 设计参数调整指南 习惯了python后&#xff0c;matlab逐…

mac电脑.sh文件,用来清除git当前分支

#!/bin/bashecho "正在检查Git仓库..." if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; thenecho "错误&#xff1a;当前目录不是Git仓库&#xff01;"exit 1 fiecho "警告&#xff1a;这将丢弃所有未提交的更改和本地提交&am…

Bash (Bourne Again SHell)

Unix/Linux 系统中最常用的命令行解释器之一&#xff0c;它是原始 Bourne shell (sh) 的增强版本。以下是 Bash 的详细解释&#xff1a; 1. Bash 基础 1.1 什么是 Bash 一个命令行解释器&#xff0c;用于执行用户输入的命令支持脚本编程&#xff0c;可以编写复杂的自动化任务…

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…

AIStor 的模型上下文协议 (MCP) 服务器: 工作原理

在本系列的前几篇博文中&#xff0c;我们讨论了MinIO AIStor 模型上下文协议 (MCP) 服务器的用户级和管理员级功能。在第一篇博文中&#xff0c;我们学习了如何查看存储桶的内容、分析对象并标记它们以便将来处理。在第二篇博文中&#xff0c;我们还学习了如何使用管理员命令以…