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

本文主要是介绍C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

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


前言

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.编译器生成一个默认赋值运算符重载

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

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

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

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

#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++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍。

这篇关于C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085184

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}