C++——类和对象(3)

目录

1. 拷贝构造

1.1 概念

1.2 特性

​编辑

2. 赋值重载 和 运算符重载

2.1 运算符重载

 2.2 赋值重载


此篇文章讲解六个默认成员函数中的  拷贝构造和赋值重载 


1. 拷贝构造

1.1 概念

拷贝构造:

  •                 在创建对象的时候用已经创建好的对象去初始化一个新对象;
  •                 只有一个形参,在用已存在的类类型对象创建新对象时由编译器自动调用。

1.2 特性

  • 拷贝构造是构造函数的一个重载形式;
  • 拷贝构造的参数只能有一个,且一般为类对象的常引用形式(常引用是因为不想传过来的对象被修改),不能传值,这样会引起无穷递归,编译器会直接报错;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_day = d._day;_month = d._month;_year = d._year;}void Print(){cout << "今天是" << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year = 10;int _month = 10;int _day = 10;
};
  • 当对象采用传值调用的时候,不会把对象直接传过去,编译器会帮你调用类里面的拷贝构造;

  • 如果拷贝构造中的参数也是值而不是引用,则会发生无穷递归:

  •  若类中未显式定义拷贝构造,则编译器会自动生成一个拷贝构造,这个构造是值拷贝(浅拷贝),内置类型会帮你全部拷贝(按字节),自定义类型会帮你调用它的拷贝构造。
class Time
{
public:Time(const Time& t){_hour = t._hour;_min = t._min;_sec = t._sec;cout << "this is time structor!" << endl;}Time(int hour = 1, int min = 1, int sec = 1){_hour = hour;_min = min;_sec = sec;}
private:int _hour;int _min;int _sec;};class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year = 10;int _month = 10;int _day = 10;Time _t;
};int main()
{Date d1;Date d2(d1);
}

  • 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的,如果像 Stack:
class Stack
{
public:Stack(int capacity){int* tmp = (int*)malloc(sizeof(int) * capacity);if (tmp == nullptr){perror("malloc error");exit(-1);}_arr = tmp;_size = 0;_capacity = capacity;}void Push(int x){//CheckCapacity()_arr[_size++] = x;}~Stack(){if (_arr != nullptr){free(_arr);_arr = nullptr;}_size = _capacity = 0;}private:int* _arr;int _size;int _capacity;
};int main()
{Stack st1(10);Stack st2(st1);}

 

  • st2 拷贝构造st1 ,这里是编译器默认生成的拷贝构造,默认是值拷贝,那么 不管是 int 类型的 size,capacity,还是指针类型的 arr 都会拷贝 st1 的,这样就会导致 st2 中的 _arr 和 st1 中的 _arr 指向同一块空间 ,在析构的时候 st2 会先 free() 那块 malloc 的空间,再析构 st1 的时候,free() 一块已释放的空间,那就会报错。

所以应该添加一份拷贝构造(深拷贝):

class Stack
{
public:Stack(int capacity){int* tmp = (int*)malloc(sizeof(int) * capacity);if (tmp == nullptr){perror("malloc error");exit(-1);}_arr = tmp;_size = 0;_capacity = capacity;}Stack(const Stack& st){_size = st._size;_capacity = st._capacity;int* tmp = (int*)malloc(sizeof(int) * _capacity);if (tmp == nullptr){perror("malloc error");exit(-1);}_arr = tmp;}void Push(int x){//CheckCapacity()_arr[_size++] = x;}~Stack(){if (_arr != nullptr){free(_arr);_arr = nullptr;}_size = _capacity = 0;}private:int* _arr;int _size;int _capacity;
};int main()
{Stack st1(10);Stack st2(st1);}

拷贝构造函数典型调用场景:

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

 

  • 为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

2. 赋值重载 和 运算符重载

2.1 运算符重载

 运算符重载:

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

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。

例如:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_day = d._day;_month = d._month;_year = d._year;}bool operator==(const Date& d){return _year == d._year &&_month == d._month &&_day == d._day;}void Print(){cout << "今天是" << _year << "年" << _month << "月" << _day<< "日" << endl;}
private:int _year = 10;int _month = 10;int _day = 10;
};int main()
{Date d1(2024, 3, 17);Date d2(d1);bool ret1 = (d1 == d2);bool ret2 = (d1.operator==(d2));cout << ret1 << " " << ret2 << endl;
}

 2.2 赋值重载

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回 *this :要复合连续赋值的含义。
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_day = d._day;_month = d._month;_year = d._year;}bool operator==(const Date& d){return _year == d._year &&_month == d._month &&_day == d._day;}Date& operator=(const Date& d){if (this != &d){_day = d._day;_year = d._year;_month = d._month;}return *this;}void Print(){cout << "今天是" << _year << "年" << _month << "月" << _day<< "日" << endl;}
private:int _year = 10;int _month = 10;int _day = 10;
};//int main()
//{
//	Date d1(2024, 3, 17);
//	Date d2(d1);
//	bool ret1 = (d1 == d2);
//	bool ret2 = (d1.operator==(d2));
//	cout << ret1 << " " << ret2 << endl;
//}int main()
{Date d1(2024, 3, 17);Date d2;d2.operator=(d1);d2.Print();d1.Print();bool ret1 = (d1 == d2);bool ret2 = (d1.operator==(d2));cout << ret1 << " " << ret2 << endl;
}
  • 赋值重载不能作为全局函数,只能重载成类的成员函数
// 错误示范
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

原因:如果不在类中定义赋值重载,那么编译器会自动生成一份,这是我们如果再类外声明             了一份,就会和类中默认的产生冲突,所以赋值重载只能是类的成员函数。

  • 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类赋值运算符重载完成赋值。
  • 如果编译器能够自动生成那么还需要我们去编写赋值重载吗?这里其实和拷贝构造类似,如果不是动态开辟的空间,那么无所谓,但是如果成员变量有需要动态开辟的空间,那么就需要自己编写赋值重载,例如栈类:

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

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

相关文章

解决分布式事务,Seata真香!

年IT寒冬&#xff0c;大厂都裁员或者准备裁员&#xff0c;作为开猿节流主要目标之一&#xff0c;我们更应该时刻保持竞争力。为了抱团取暖&#xff0c;林老师开通了《知识星球》&#xff0c;并邀请我阿里、快手、腾讯等的朋友加入&#xff0c;分享八股文、项目经验、管理经验等…

Java八股文(MyBatis Plus)

Java八股文のMyBatis Plus MyBatis Plus MyBatis Plus MyBatis Plus 是什么&#xff1f;它与 MyBatis 有什么区别&#xff1f; MyBatis Plus 是基于 MyBatis 进行扩展的一款持久层框架&#xff0c;它提供了一系列增强功能&#xff0c;简化了 MyBatis 的使用。 与 MyBatis 相比…

zookeeper快速入门四:在java客户端中操作zookeeper

系列文章&#xff1a; zookeeper快速入门一&#xff1a;zookeeper安装与启动-CSDN博客 zookeeper快速入门二&#xff1a;zookeeper基本概念-CSDN博客 zookeeper快速入门三&#xff1a;zookeeper的基本操作 先启动zookeeper服务端。 在maven引入zookeeper依赖。 <depende…

Acwing-基础算法课笔记之动态规划(计数类DP)

Acwing-基础算法课笔记之动态规划&#xff08;计数类DP&#xff09; 一、整数划分1、定义2、完全背包的做法代码示例&#xff08;1&#xff09;过程模拟&#xff08;2&#xff09;代码示例 3、计数类DP的做法&#xff08;1&#xff09;过程模拟&#xff08;2&#xff09;闫氏DP…

疑难杂症!handleSubmit does not execute onSubmit function

背景 今天在写Nextjs代码的时候&#xff0c;发现一个问题&#xff0c;我使用react-use-form的表单&#xff0c;点击提交按钮的时候&#xff1a;onSubmit没有被触发&#xff01;&#xff01; 于是排查 源代码如下&#xff1a; "use client"import { AddLinkReques…

ros小问题之差速轮式机器人轮子不显示(rviz gazebo)

在rviz及gazebo练习差速轮式机器人时&#xff0c;很奇怪&#xff0c;只有个机器人的底板及底部的两个万向轮&#xff0c;如下图&#xff0c; 后来查看相关.xacro文件&#xff0c;里面是引用包含了轮子的xacro文件&#xff0c;只需传入不同的参数即可调用生成不同位置的轮子&…

数据结构——lesson8二叉树的实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

如何使用人工智能打造超用户预期的个性化购物体验

回看我的营销职业生涯&#xff0c;我见证了数字时代如何重塑客户期望。从一刀切的方法过渡到创造高度个性化的购物体验已成为企业的关键。在这个客户期望不断变化的新时代&#xff0c;创造个性化的购物体验不再是奢侈品&#xff0c;而是企业的必需品。人工智能 &#xff08;AI&…

UnityShader(十六)凹凸映射

前言&#xff1a; 纹理的一种常见应用就是凹凸映射&#xff08;bump mapping&#xff09;。凹凸映射目的就是用一张纹理图来修改模型表面的法线&#xff0c;让模型看起来更加细节&#xff0c;这种方法不会改变模型原本的顶点位置&#xff08;也就是不会修改模型的形状&#xf…

资深老鸟经验,性能测试-性能指标分析总结,一篇策底概全...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试指标 1、…

146 Linux 网络编程2 ,Socket编程,如何创建Linux 服务器 和linux 客户端

IPport 就是一个程序在网络上的身份证号码。 这意味着我们需要如果写一个服务器&#xff0c;至少需要将这台服务器的ip 和 端口号写到程序里面。 实际上更细化的说&#xff1a;应该是将这三都写进程序里面 &#xff1a; IP类型&#xff08;IPV4或者IPV6&#xff09;&#xff…

Anaconda安装proplot库

看了一下Anaconda中的环境&#xff0c;现在我有4个&#xff0c;其中gee是一个虚拟环境 因此一般在prompt中装库时要先进入其中一个虚拟环境 conda activate geepip install proplot --no-deps下完了之后&#xff0c;发现版本不对应 conda install matplotlib3.4.3

面试算法-39-删除链表的倒数第 N 个结点

题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 解 class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {L…

【Linux】进程信号{初识信号/常见的信号/中断信号/信号的产生}

文章目录 0.浅谈中断信号1.初识信号2.中断信号3.信号的产生测试&#xff1a;SIGINT 4.core dump核心转储5.系统接口产生信号5.1kill给指定发5.2raise向自己发5.3abort自己给自己发6 6.由于软件条件不满足产生信号6.1SIGPIPE6.2SIGALRM 7. 硬件异常产生信号7.1除零错误7.2野指针…

云服务器容器常用操作系统介绍

常用操作系统介绍 开源软件国内镜像源Alpine操作系统介绍镜像源修改镜像源apk包管理器 Debian操作系统介绍镜像源修改镜像源apt包管理器 ubuntu操作系统介绍修改镜像源apt包管理器 CentOS操作系统介绍修改镜像源yum包管理器 开源软件国内镜像源 名称地址南京大学mirror.nju.ed…

奇酷网络董事长吴渔夫:AI时代的工作节奏

奇酷网络董事长吴渔夫今日分享&#xff1a; 1、从2024年春节后上班至今&#xff0c;我每天都是工作16小时的。除了睡觉时间段&#xff0c;其它时间都在工作&#xff1b;连吃个饭也是一边吃一边看手机&#xff0c;阅读AI新闻、学习Sora和AI知识的。 2、我的工作方式&#xff0…

22 OpenCV 直方图计算

文章目录 直方图概念split 通道分离函数calcHist 计算直方图normalize 归一化函数示例 直方图概念 上述直方图概念是基于图像像素值&#xff0c;其实对图像梯度、每个像素的角度、等一切图像的属性值&#xff0c;我们都可以建立直方图。这个才是直方图的概念真正意义&#xff0…

PHP魔术方法详解

php魔术方法是一些特殊的方法&#xff0c;由特定的环境来进行触发。 这些魔术方法让开发者能够更好地控制对象的行为&#xff0c;特别是在处理不常见的操作或者需要自动化处理某些任务时非常有用。 1、_construct()构造函数&#xff1a; <?php highlight_file(__FILE__);…

【静夜思】为什么我们会如此喜欢夜晚呢

作为一名大学生&#xff0c;熬夜对我来说已是常态。每天都是近乎一点钟才有困意&#xff0c;觉得应该上床睡觉了&#xff0c;即使明天早八&#xff0c;即使明天有很多课。我也观察过身边的朋友&#xff0c;他们中大多数也和我一样&#xff0c;基本都是在12点过后才入睡。当今的…

DDL - 建立数据库,建表代码版(Way 2)

一、DB操作 show databases; create database DBOFRYX; drop database DBOFRYX; use DBOFRYX; 二、表操作&#xff08;表和表结构、字段是A、B两姐妹&#xff09; (1) use DBOFRYX; show tables; (2) create table TABOFRYX( name varchar(50) comment "姓名"…