C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍

文章目录

  • 前言
  • 一、拷贝构造函数
    • 1. 概念
    • 2. 特征
    • 3. 编译器生成默认拷贝构造函数
    • 4. 拷贝构造函数典型使用场景
  • 二、运算符重载函数
  • 三、赋值运算符重载函数
    • 1. 赋值运算符重载格式
    • 2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
    • 3.编译器生成一个默认赋值运算符重载
    • 4. 运算符重载函数和赋值运算符重载函数的调用
  • 四、前置++和后置++重载
  • 总结


前言

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍


一、拷贝构造函数

在实际运用中,我们有时候需要操作一个实例化好的类,但是又不想改变这个类,这时候,我们就需要拷贝构造函数。

1. 概念

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

2. 特征

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

  1. 拷贝构造函数构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

拷贝构造函数是构造函数的一个重载形式,所以拷贝构造函数的函数名与类名相同,参数与构造函数不同,无返回值。

如下图:

  • 采用传值调用,会引发无穷递归,编译器强制检查报错。
    在这里插入图片描述

  • 应该采用类类型对象的引用,如下:

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}~Date(){_year = 0;_month = 0;_day = 0;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 6, 22);Date d2(d1);return 0;
}

在这里插入图片描述

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

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

简单来说就是,不显示定义,编译器默认生成一个构造函数

  • 对于内置类型会进行值拷贝(浅拷贝)。
  • 对于自定义类型会调用它的拷贝构造函数。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}~Date(){_year = 0;_month = 0;_day = 0;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 9, 1);Date d2(d1);return 0;
}

在这里插入图片描述

编译器默认生成的拷贝构造函数是值拷贝(浅拷贝),但是有些情况下我们需要深拷贝。比如:
对一个栈,我们不能直接浅拷贝,程序会崩溃。原因如下:

浅拷贝会将一个栈动态申请的空间的地址拷贝个另一个栈的指针,所以两个栈对象的指针指向同一块空间,析构函数会对同一块空间释放两次,程序会崩溃。

#include <iostream>
using namespace std;class Stack
{
public:Stack(int capacity = 4){_a = (int*)malloc(sizeof(int) * capacity);if (nullptr == _a){perror("Stack malloc");return;}_top = 0;_capacity = capacity;}void Push(int x){// 增容_a[_top] = x;_top++;}~Stack(){if (_top){free(_a);_a = nullptr;_top = 0;_capacity = 0;}}
private:int* _a;int _top;int _capacity;
};int main()
{Stack st1(10);st1.Push(1);st1.Push(2);st1.Push(3);st1.Push(4);Stack st2(st1);return 0;
}

在这里插入图片描述


在这里插入图片描述

  • 所以,这种Stack的类对象,我们应该自己显示定义拷贝构造函数。
#include <iostream>
using namespace std;class Stack
{
public:Stack(int capacity = 4){_a = (int*)malloc(sizeof(int) * capacity);if (nullptr == _a){perror("Stack malloc");return;}_top = 0;_capacity = capacity;}Stack(const Stack& st){_a = (int*)malloc(sizeof(int) * st._capacity);if (nullptr == _a){perror("Stack malloc");return;}memcpy(_a, st._a, sizeof(int) * st._top);_top = st._top;_capacity = st._capacity;}void Push(int x){// 增容_a[_top] = x;_top++;}~Stack(){if (_top){free(_a);_a = nullptr;_top = 0;_capacity = 0;}}
private:int* _a;int _top;int _capacity;
};int main()
{Stack st1(10);st1.Push(1);st1.Push(2);st1.Push(3);st1.Push(4);Stack st2(st1);return 0;
}

在这里插入图片描述

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

4. 拷贝构造函数典型使用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

二、运算符重载函数

在介绍运算符重载之前先来看看运算符重载和函数重载的比较

  • 函数重载和运算符重载都用了“重载”一词,但是两者重载没有关系
  • 函数重载是为了函数名相同,函数参数不同的函数可以同时存在
  • 运算符重载是为了自定义类型的数据也可以像内置类型一样使用运算符进行比较或赋值等

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .*、 ::、 sizeof 、?: . 注意以上5个运算符不能重载**
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}~Date(){_year = 0;_month = 0;_day = 0;}
//private:int _year;int _month;int _day;
};bool operator<(const Date& x1, const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day){return true;}else{return false;}
}bool operator>(const Date& x1, const Date& x2)
{if (x1._year > x2._year){return true;}else if (x1._year == x2._year && x1._month > x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day > x2._day){return true;}else{return false;}
}int main()
{Date d1(1945, 8, 15);Date d2(1949, 10, 1);cout << operator<(d1, d2) << endl;cout << (d1 < d2) << endl;cout << operator>(d1, d2) << endl;cout << (d1 > d2) << endl;return 0;
}

在这里插入图片描述

使用运算符重载时,调用运算符重载函数,也可以直接使用运算符比较。

	cout << operator<(d1, d2) << endl;cout << (d1 < d2) << endl;cout << operator>(d1, d2) << endl;cout << (d1 > d2) << endl;
  • 一般建议直接使用运算符比较。

上述运算符重载到了全局中,运算符重载也可以重载到类中。

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}bool operator<(const Date& x2){if (_year < x2._year){return true;}else if (_year == x2._year && _month < x2._month){return true;}else if (_year == x2._year && _month == x2._month && _day < x2._day){return true;}else{return false;}}~Date(){_year = 0;_month = 0;_day = 0;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1945, 8, 15);Date d2(1949, 10, 1);cout << d1.operator<(d2) << endl;cout << (d1 < d2) << endl;return 0;
}

在这里插入图片描述

三、赋值运算符重载函数

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}~Date(){_year = 0;_month = 0;_day = 0;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1945, 8, 15);Date d2(1949, 10, 1);//d2.operator=(d1);d2 = d1;return 0;
}

在这里插入图片描述

赋值运算符可以选择调用赋值运算符重载函数,也可以直接使用赋值运算符,一般建议直接使用赋值运算符。

	d2.operator=(d1);d2 = d1;

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

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

class Date
{
public:Date(int year = 1945, int month = 8, int day = 15){_year = year;_month = month;_day = day;}~Date(){_year = 0;_month = 0;_day = 0;}
//private:int _year;int _month;int _day;
};Date& operator=(Date& left, const Date& right)
{left._year = right._year;left._month = right._month;left._day = right._day;return left;
}

在这里插入图片描述

3.编译器生成一个默认赋值运算符重载

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

注意:
内置类型成员变量是直接赋值的,
而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

4. 运算符重载函数和赋值运算符重载函数的调用

  • 内置类型使用运算符进行操作直接转换成指令进行操作。
  • 自定义类型若直接使用运算符操作,会调用对应的运算符重载函数。

四、前置++和后置++重载

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1328, int month = 1, int day = 4){_year = year;_month = month;_day = day;}// 前置++Date& operator++(){_day += 1;return *this;}// 后置++Date operator++(int){Date tmp(*this);_day += 1;return tmp;}
private:int _year;int _month;int _day;
};int main()
{Date d;Date d1(1949, 10, 1);d = d1++; // d: 1949 10 1   d1: 1949 10 2d = ++d1; // d: 1949 10 3   d1: 1949 10 3return 0;
}
  • 前置++
    前置++:返回+1之后的结果
    注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
  • 后置++
    前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
    C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
    注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1而temp是临时对象,因此只能以值的方式返回,不能返回引用

总结

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍。

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

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

相关文章

Android记录9--实现转盘效果

自定义View /2013.10.16_TurnPlate_Demo/src/com/wwj/turnplate/TurnPlateView.java package com.wwj.turnplate; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; im…

深入了解RTMP推流技术:视频汇聚EasyCVR低延迟与高稳定性分析

RTMP&#xff08;Real Time Messaging Protocol&#xff09;视频推流技术&#xff0c;作为音视频传输领域的关键技术之一&#xff0c;已经在直播、视频会议、在线教育等多个场景中得到了广泛应用。RTMP以其独特的优势&#xff0c;为实时音视频传输提供了高效、稳定的解决方案。…

C++之std::queue::emplace

std::queue::emplace 是 C STL 中 std::queue 容器的成员函数&#xff0c;它用于在队列的末尾就地构造一个新元素。这个函数类似于 std::queue::push&#xff0c;但是 emplace 允许你通过传递参数来构造元素&#xff0c;而不需要显式地创建一个元素对象。 理解 std::queue::em…

NGINX_十五 nginx 的平滑升级(了解)

十五 nginx 的平滑升级(了解) 1 为什么要对 nginx 平滑升级 随着 nginx 越来越流行&#xff0c;并且 nginx 的优势也越来越明显&#xff0c;nginx 的版本迭代也来时加速模式&#xff0c;1.9.0版本的nginx更新了许多新功能&#xff0c;例如 stream 四层代理功能&#xff0c;伴…

ffmpeg音视频开发从入门到精通——ffmpeg实现音频抽取

文章目录 FFmpeg 实现音频流抽取1. 包含FFmpeg头文件与命名空间声明2. 主函数与参数处理3. 打开输入文件4. 获取文件信息5. 查找音频流6. 分配输出文件上下文7. 猜测输出文件格式8. 创建新的音频流9. 打开输出文件10. 写入文件头信息11. 读取并写入音频数据12. 写入文件尾部信息…

三维渲染背面剔除

三维渲染中的背面剔除 背面剔除&#xff08;Backface Culling&#xff09;是一种用于优化三维渲染性能的技术&#xff0c;通过剔除那些在当前视角下不可见的多边形&#xff0c;减少需要处理的几何体数量&#xff0c;从而提高渲染效率。 基本概念 在三维渲染中&#xff0c;物…

重磅!2024年最新影响因子(生态学/林学/土壤学/遥感/微生物/环境科学/植物科学) 收藏版!

2024年最新影响因子正式揭晓&#xff01;2024年6月20日&#xff0c;Clarivate Analytics&#xff08;科睿唯安&#xff09;发布了各大SCI期刊的2023年影响因子。从最新结果看&#xff0c;今年的影响因子继续“普跌”&#xff0c;其中顶刊Nature和Science均有下降&#xff0c;分…

2024.06.23【读书笔记】丨生物信息学与功能基因组学(第十七章 人类基因组 第三部分)【AI测试版】

第三部分:人类基因组的深入分析与比较基因组学 摘要: 本部分基于2001年国际人类基因组测序联盟(IHGSC)发布的人类基因组测序及分析草图,从生物信息学角度深入讨论了人类基因组的结构特征和分析方法。同时,提及了塞莱拉公司(Celera Genomics)版本的人类基因组草图及其…

Java中的序列化与反序列化

Java中的序列化与反序列化 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java开发中&#xff0c;序列化和反序列化是处理对象持久化和数据传输的重要机制。…

【自我提升】提升能量书籍

《原子习惯》 (Atomic Habits) - 詹姆斯克利尔 (James Clear)&#xff1a; 核心思想&#xff1a;微小的习惯改变可以带来显著的生活变化。方法&#xff1a; 将大目标拆分为可管理的小习惯。使用“习惯堆叠”技术&#xff0c;将新习惯与已有习惯结合。创建支持性环境&#xff0c…

ubuntu永久换镜像源详细方法

1.查看ubuntu的版本&#xff0c;不同的版本对应的不同的镜像源&#xff08;下面会讲到&#xff0c;先按步骤操作即可&#xff09; cat /etc/issue 2.先备份一个&#xff0c;防止更改错误 cp /etc/apt/sources.list /etc/apt/sources.list.backup 3.备份好之后删除原有的sour…

如何卸载宝塔面板?

宝塔官方有提供宝塔面板的卸载命令&#xff0c;使用这个卸载命令&#xff0c;我们就能将宝塔面板卸载掉。 这里有一点需要注意的&#xff0c;如果卸载宝塔面板的同时&#xff0c;也希望将 Nginx、MySQL、PHP 等组件卸载掉&#xff0c;那么我们应该先在宝塔面板里面卸载掉以上软…

Android图片圆角转换 RoundedImageView开源项目 小记(1)

android:background“#7f000000” android:paddingLeft“8dp” android:paddingRight“8dp” android:textAppearance“?android:attr/textAppearanceMediumInverse” /> <TextView android:id“id/textView1” android:layout_width“wrap_content” android:la…

【Gradio】从 BigQuery 数据创建实时仪表板

Google BigQuery 是一个基于云的服务&#xff0c;用于处理非常大的数据集。它是一个无服务器且高度可扩展的数据仓库解决方案&#xff0c;使用户能够使用类 SQL 查询分析数据。 在本教程中&#xff0c;我们将向您展示如何在 Python 中查询 BigQuery 数据集&#xff0c;并使用 g…

SpringBoot 快速入门(保姆级详细教程)

目录 一、Springboot简介 二、SpringBoot 优点&#xff1a; 三、快速入门 1、新建工程 方式2&#xff1a;使用Spring Initializr创建项目 写在前面&#xff1a; SpringBoot 是 Spring家族中的一个全新框架&#xff0c;用来简化spring程序的创建和开发过程。SpringBoot化繁…

delphi屏幕分辩率及缩放自适应解决方案

以下在win10上做的测试&#xff0c;win7未试验&#xff0c;有需要的可以自行测试。 首先应用程序Application.Manifest中设置为&#xff1a;per Monitor V2. 不然有可能窗体收不到屏幕变动通知。 Delphi10.4版本中&#xff0c;窗体有Scaled属性&#xff0c;true为默认&#x…

Linux第十一章:Samba文件共享服务

实验案例&#xff1a;构建Samba文件共享服务器 1、实验环境 根据公司信息化建设要求&#xff0c;公司需要在局域网内部搭建一台文件服务器&#xff0c;便于对数据的集中管理和备份。考虑服务器的运行效率及稳定性、安全性问题&#xff0c;选择在Centos系统中构建Samba服务器以…

Bootloader -- U-Boot 介绍

Bootloader -- U-Boot 介绍 1 介绍1.1 概述1.2 知名 BootloaderLILO (Linux Loader)GRUB (GNU GRand Unified Bootloader)LoadlinROLO (Rockbox Loader)EtherbootLinuxBIOS (现在叫 coreboot)BLOBU-BootRedBoot 1.3 BootLoader 和 Monitor 区别1.4 U-Boot 的源码结构1.5 U-Boot…

idea导入文件里面的子模块maven未识别处理解决办法

1、File → Project Structure → 点击“Modules” → 点击“” → “Import Model” 2、可以看到很多子模块&#xff0c;选择子模块下的 pom.xml 文件导入一个一个点累死了&#xff0c;父目录下也没有pom文件 解决办法&#xff1a;找到子模块中有一个pom.xml文件&#xff0c;…

数据迁移到 Django 模型表:详尽指南

数据迁移是许多应用程序开发过程中必不可少的一部分。在这篇文章中&#xff0c;我们将详细分析和总结如何通过一个定制的 Django 管理命令&#xff0c;将数据从 MySQL 数据库迁移到 Django 模型表中。这种方法可以确保数据在多个数据库之间有效且安全地迁移&#xff0c;同时避免…