【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

这篇具有很好参考价值的文章主要介绍了【C++基础(六)】类和对象(中) --拷贝构造,运算符重载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++初阶之路⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言


1. 前言

本章重点:

本篇文章将详细讲解拷贝构造函数
和运算符重载,并介绍const成员的概念

拷贝构造函数和运算符重载
是类和对象中六大默认成员函数
的其中两个

拷贝构造类似于构造一个一模一样的你

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言


2. 拷贝构造函数

我们在写代码的时候会遇见这种场景:

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(2023,7,30);
	 Date d2(d1);//用d1初始化d2
	 return 0;
}

使用一个已经存在的对象初始化
一个正在定义的对象就是在拷贝构造!

拷贝构造函数特征:

  • 拷贝构造是构造函数的一个重载形式
  • 拷贝构造函数只能有一个参数且必须
    是类类型对象的引用!

比如Date类的拷贝构造:

 Date(const Date& d)
 {
	 _year = d._year;
	 _month = d._month;
	 _day = d._day;
 }

2.1 对拷贝构造函数参数的思考

为什么拷贝构造函数的参数
一定要是:类类型对象的引用?

因为拷贝构造是用另一个对象初始化
所以是类类型对象可以理解
但是为啥必须传引用?

这是因为不传引用会引起无限递归!

可以这样理解无限递归:

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言
假如加上了引用,调用拷贝构造时
传对象的别名进函数,就不会有
形参拷贝一份实参的过程,就不会死递归!

所以拷贝构造函数一定要加上引用!


2.2 默认拷贝构造函数

如果用户不显示写拷贝构造函数
编译器会自动生成一个默认构造函数

默认构造函数会对内置类型完成:
值拷贝(浅拷贝)

默认构造函数会去调用自定义
类型的构造函数完成拷贝

对于内置类型来说,浅拷贝可能会有问题!

举个例子:

class Stack
{
public:
 Stack(size_t capacity = 10)//构造函数
 {
	 _array = (DataType*)malloc(capacity * sizeof(DataType));
	 if (nullptr == _array)
	 {
	 perror("malloc申请空间失败");
	 return;
	 }
	 _size = 0;
	 _capacity = capacity;
 }
 void Push(const int& data)//插入函数
 {
	 _array[_size] = data;
	 _size++;
 }
 
 ~Stack()//析构函数
 {
	 if (_array)
	 {
		 free(_array);
		 _array = nullptr;
		 _capacity = 0;
		 _size = 0;
	 }
 }

private:
	 int *_array;
	 size_t _size;
	 size_t _capacity;
};

int main()
{
	Stack s1(10);
	s1.push(1);
	s1.push(2);
	s3.push(3);
	s3.push(4);
	s3.push(5);
	Stack s2(s1);
	retrn 0;
}

stack类没有显示写拷贝构造函数
编译器会自动生成默认拷贝构造函数

默认构造函数是按照字节方式直接拷贝的

所以对象s1和s2的关系图如下:

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言

array指向的空间是动态开辟的
是在堆区存储的,使用值拷贝的方式
s1和s2中的array指向的空间相同!

这么做会有啥问题?

当s1和s2生命周期结束时
会分别调用它们各自的析构函数
然而两个对象中的指针指向的空间相同
析构函数会调用两个free释放空间!
同一份空间释放两个就会出错!


2.3 对拷贝构造函数的总结

  • 拷贝构造函数的参数必须是引用

  • 若类中没有涉及到空间申请
    则默认拷贝构造就够用了

  • 下面这两种写法都是在拷贝构造:

Date d1(2023,7,30);
Date d2(d1);
Date d3 = d1;

拷贝构造的典型应用场景:

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

3. 运算符重载

在日期类中定义一个对象:

Date d1(2023,7,31);

假设现在想要计算100天
后是几月几号的话,直接使用+
无法实现这个功能!

Date d2 = d1+100; //普通的加号不能实现此功能

C++中,普通的加号只适用于内置类型
而自定义类型的加号种类太多
系统没有自行实现,需要用户自己写!

要自己实现的运算符需要运算符重载!

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

使用关键字: operator

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

举例说明日期类+号的重载:

Date operator+(Date d1, int x);

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言


3.1 对运算符重载的思考

运算符重载是针对自定义类型的
所以函数参数中必须有一个类类型参数

但是,如果运算符重载写在类外
它就不能访问类中的私有成员!

所以常常将运算符重载写在类中!

比如:

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;
};

写在类中时,类类型参数可以省略!

因为类中函数默认有this指针
指针this就代表了此类对象了!


3.2 特殊的赋值运算符

赋值运算符十分特殊
若我们不显示写,编译器会自动生成一个

所以赋值运算符只能写在类中
如果显示写在了类外的话
编译器会自动生成一个赋值函数
这就和你自己写的冲突了!

Date& operator=(const Date& d)
 {
 if(this != &d)
 {
     _year = d._year;
     _month = d._month;
     _day = d._day;
 }
 return *this;
 }

注:this是类对象的地址
*this就是类对象本身

赋值运算符返回类对象的原因是:
多次连续赋值

Date d1(2023,7,31);
Date d2;
Date d3;
d2 = d3 = d1;//连续赋值

拓展:

编译器自动生成的默认赋值运算符重载,以值的方式逐字节拷贝。

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

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


3.3 前置++和后置++

对于++的运算符重载比较有争议
因为前置和后置是两种不同的函数!

C++为了区分前置和后置++
在后置++的函数中多加一个int
类型的参数来区别前置++

比如:

前置++:

Date& operator++();

后置++:

Date& operator++(int);

注意,虽然后置++多了一个参数
但是这个参数完全没用!
只用于区别前置++

++运算符的使用:

Date d1(2023,7,31);
//前置++
++d1;
//后置++
d1++;

虽然后置++多了一个参数
但是不需要我们显示传参
编译器会自动帮助我们传参!


3.4 运算符重载再理解

先看以下日期类代码:

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;
 }
 Date& operator=(const Date& d)
 {
	 if(this != &d)
	 {
	     _year = d._year;
	     _month = d._month;
	     _day = d._day;
	 }
	 return *this;
 }
private:
 int _year;
 int _month;
 int _day;
};

现在定义类对象进行操作:

Date d1(2023,7,31);
Date d2(2023,7,30);
if(d1==d2)
{
	cout<<"d1和d2相同";
}
Date d3;
d3 = d1;

上面的代码中调用了两个运算符重载函数
operator=operator==

实际上调用 == 时是这样的:

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言
调用 = 时是这样的:

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载,C++初阶之路,c++,java,开发语言


4. 总结以及拓展

写运算符重载函数时,尽量使用引用传参

const Date& d

若传值传参,会调用拷贝构造,效率很低

有几个操作符不能被运算符重载:

  • .*
  • ::
  • sizeof
  • ?:
  • .

C++中拷贝构造和赋值运算符容易混淆

Date d1(2023,7,31);
Date d2 = d1;
Date d3;
d3 = d1;

d2=d1是调用了拷贝构造函数
d3=d1是调用了赋值运算符重载

关于更多拷贝构造和赋值的关系
可以参考这篇文章:

拷贝构造和赋值运算符重载文章来源地址https://www.toymoban.com/news/detail-625983.html


🔎 下期预告:类和对象(下) 🔍

到了这里,关于【C++基础(六)】类和对象(中) --拷贝构造,运算符重载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】四、类和对象(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】三、类和对象 (面向过程、class类、类的访问限定符和封装、类的实例化、类对象模

    2024年02月05日
    浏览(40)
  • 【C++学习】类和对象 | 拷贝构造 | 探索拷贝构造函数为什么需要引用传参 | 深拷贝 | 初识运算符重载

    上一篇文章我们开始学习类内的默认成员函数, 这里是传送门,有兴趣可以去看看:http://t.csdn.cn/iXdpH 这篇文章我们继续来学习类和对象的知识。 目录 写在前面: 1. 拷贝构造 2. 拷贝构造函数为什么需要引用传参? 3. 深拷贝 4. 初识运算符重载 写在最后: 我们在创建一个对

    2024年02月11日
    浏览(37)
  • 【C++】类和对象③(类的默认成员函数:拷贝构造函数 | 赋值运算符重载)

    🔥 个人主页: Forcible Bug Maker 🔥 专栏: C++ 目录 前言 拷贝构造函数 概念 拷贝构造函数的特性及用法 赋值运算符重载 运算符重载 赋值运算符重载 结语 本篇主要内容:类的6个默认成员函数中的 拷贝构造函数 和 赋值运算符重载 在上篇文章中我们讲到了类的默认成员函数的

    2024年04月17日
    浏览(34)
  • c++类和对象(拷贝构造、运算符重载、初始化列表、静态成员、友元等)

    拷贝构造函数的特征: 1、拷贝构造函数是构造函数的一个重载形式; 2、拷贝构造函数的参数只有一个且必须是同类类型对象的引用, 使用传值方式编译器直接报错 ,因为会引发无穷递归调用。 在c++中自定义类型 传值传参 的时候要调用拷贝构造函数。 3、若未显式定义,

    2024年02月15日
    浏览(27)
  • 【C++初阶】类与对象:6大默认成员函数------拷贝构造和赋值运算符重载

      拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰) ,在用已存在的类类型对象创建新对象时由编译器自动调用。 1. 拷贝构造函数是 构造函数的一个重载形式 ; 2. 拷贝构造函数的 参数只有一个且必须是类类型对象的引用 ,使用传值方式编

    2024年02月03日
    浏览(34)
  • c++拷贝构造与赋值运算符重载

    目录 目录:         1:拷贝构造         2:赋值运算符重载                  前言:在上一章我们已经学习过了,构造与析构这两个默认成员函数了,接下来让我们一起来学习另外两个重要的默认成员函数。         首先让我们来讲一下默认成员函数这个概念,所谓

    2024年02月08日
    浏览(41)
  • 【C++】:拷贝构造函数和赋值运算符重载

    拷贝构造函数是特殊的构造函数。 是用一个已经存在的对象,赋值拷贝给另一个新创建的已经存在的对象 。 本质:用同类型的对象拷贝初始化 。 拷贝构造函数也是 特殊的成员函数 ,其特征如下: 2.1 拷贝构造函数是构造函数的一个重载形式。 2.2 拷贝构造函数的 函数名域

    2024年04月28日
    浏览(29)
  • C++拷贝构造函数与赋值运算符重载

    顾得泉: 个人主页 个人专栏: 《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》 键盘敲烂,年薪百万!        在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。        那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?  

    2024年02月22日
    浏览(35)
  • 【C++】构造函数,析构函数,拷贝构造,运算符重载,const 成员

    默认成员函数:如果不显示,编译器默认生成 构造函数:是一个特殊的 成员函数 ,函数名与类名相同,专门用于 初始化类对象 函数名与类名相同 无返回值 ,没有被声明为void类型 对象实例化时 编译器自动调用 , Date d1 ,或 Date d2(2023, 4, 21) 构造函数可以重载,一个类中可以

    2023年04月24日
    浏览(55)
  • 【C++】C++入门—初识构造函数 , 析构函数,拷贝构造函数,赋值运算符重载

    如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗? 并不是 任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数 我们实现了,编译器就不会生成了 构造函数是

    2024年02月21日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包