【c++初阶】类和对象②默认成员函数以及运算符重载初识

目录

​编辑

默认成员函数:

构造函数

构造函数的特性:

析构函数:

拷贝构造函数:

1. 拷贝构造函数是构造函数的一个重载形式。

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

默认生成的拷贝构造:

1.内置类型完成值拷贝

2. 若未显式定义,编译器会生成默认的拷贝构造函数。

3.拷贝构造函数的典型引用场景

默认赋值运算符

c++运算符重载

赋值:赋值运算符的重载:


默认成员函数:

对于上篇文章中讲解中所使用的类,一般正常使用是没有问题的,但是如果我们在使用某些类的时候,,比如我们的日期类使用时,忘记或者没有进行初始化会发生什么样的问题呢:

现象是编译通过,但是成员变量都是随机值。 严重一点可能会崩溃。

为了解决这个问题,C++中引入了构造函数:

构造函数

是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

构造函数的特性:

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。 其特征如下:

1. 函数名与类名相同。

2. 无返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。

两个默认构造函数的功能和Init函数的功能是一模一样的,但是构造函数可以默认调用:

 如何调用带参数的构造函数呢:

在创建对象的时候自动调用,

 没有参数调用的时候不可以在对象名字后面加上括号

因为无法和函数声明区分开。

编写某认构造的时候可以使用缺省参数,和无参调用构成重载但是不可以同时存在,无参调用时二者会产生歧义:

Date(int year = 2025, int mouth = 1, int day = 1)
{_year = year;_mouth = mouth;_day = day;
}
//写一个构造函数的重载://Date()
//{
//	_year = 2025;
//	_mouth = 1;
//	_day = 1;
//}

通过上面的例子我们知道,如果我们不写构造函数,使用的时候也可以打印出值,不过是随机值,这就意味着在c++中如果我们不写构造函数应该是有构造函数的,接下来我们观察一个栈的实现类:

现象:队列没写构造函数但是调用了栈的构造函数,如果写一个int size正常应该是不初始化化,初始化的原因是编译器自己做了一些优化。所以这个行为是不确定的。

C语言将数据类型分为了内置类型和自定义类型,

内置类型:int char 指针等,所以Date* p是内置类型,因为是指针,指针的本质就是地址。

自定义类型:struct class enum等等。

某认生成构造函数:内置类型成员不做处理,自定义类型会去调用他的默认构造。

vs2022现象,当一个对象的成员变量中有自定义类型和内置类型的时候,都会去初始化,但是当只有内置类型的时候又不初始化,这是由于编译器的优化导致。

上述对于两种不同类型数据的区别对待使得学习c++这门语言有些冗余,于是在C++11中做了这样的补丁,对于成员变量允许这样操作:

但是,值得注意的是这里的几个成员变量在1这里依旧是声明,并没有因为初始化而变成定义

原因: 这在类中相当于还是图纸,只有当用这个类的类型实例化一个对象的时候才会开辟空间,这里的值理解成缺省值。编译器生成某认构造的时候就可以用了。

重点:无参的构造函数和全缺省的构造函数以及编译器自己生成的构造函数,都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。也就是不传参的都是默认构造函数。三个函数不能同时存在,否则有歧义。

总结:

1.一般情况下,我们都要自己写构造函数

2.成员都是自定义类型,或者声明时给了缺省值,可以考虑让编译器自己去生成构造函数。

析构函数:

 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

 特性 析构函数是特殊的成员函数,其特征如下:

1. 析构函数名是在类名前加上字符 ~。

2. 无参数无返回值类型。

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

日期类实际上没有需要时释放的资源,可以看一下调用析构函数的过程:

~Date()
{cout << "data destory" << endl;
}

出了作用领域,,自动调用。 

对于栈的释放资源,析构函数是必要的,内存泄漏不会报错。

~Stack()
{free(_a);_capacity = _top = 0;_a = nullptr;
}

对象的开辟和销毁是由系统来进行的

编译器 生成的默认析构函数,对自定类型成员调用它的析构函数。对于栈类,我们不写析构函数,不会自己销毁,因为编译器不敢。对内置类型不做处理,对自定义类型调用其析构函数。

拷贝构造函数:

传参本质就是一个拷贝。

对于日期类的传参拷贝:

void Test(Date d)
{d.Print();
}int main()
{Date d1;Test(d1);

这是值拷贝也就是浅拷贝

对于栈类的传参如果也使用值拷贝:

可能会出现对一块空间进行多次释放的问题。

c++规定,自定义类型的对象拷贝的时候,调用一个函数,这个函数叫做拷贝构造。

Date d2(d1)  使用d1初始化d2也是拷贝构造。

1. 拷贝构造函数是构造函数的一个重载形式。

日期类的拷贝构造:

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

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

调用拷贝构造如果是传值传参的话又会引起拷贝构造。

所以会用传指针和传引用的方式。

c++规定自定义类型对象传参拷贝,必须经过拷贝构造。可以这么理解

但是引用传参和指针传参不会发生拷贝构造函数的调用。

关于栈类的拷贝,浅拷贝可能会引起析构的时候一块空间释放两次的问题,所以栈类一般定义为深拷贝:

Stack(Stack& stt)
{_a = (int*)malloc(sizeof(int) * stt._capacity);if (_a == nullptr){perror("malloc fail");exit(-1);}memcpy(_a, stt._a, sizeof(int) * stt._top);_top = stt._top;_capacity = stt._capacity;
}

对于队列类,我们没有写拷贝构造函数,自己调用了拷贝构造:

默认生成的拷贝构造:

1.内置类型完成值拷贝

自定义类型会调用这个成员的拷贝构造。(比如栈这种类型需要我们自己写一个深拷贝,浅拷贝可能会出问题)

  写拷贝构造的时候最好带上const修饰,否则会出现反向拷贝的情况。

2. 若未显式定义,编译器会生成默认的拷贝构造函数。

默认的拷贝构造函数对象按内存存储按 字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定 义类型是调用其拷贝构造函数完成拷贝的。

3.拷贝构造函数的典型引用场景

①使用已经存在的对象创建新对象

②函数参数类型为类 类型对象

③函数返回值类型为类类型对象

默认赋值运算符

对于“=”,也就是赋值运算符的运算,如果我们不显示实现编译器也会生成一个默认的。跟拷贝构造的行为类似,内置类型完成值拷贝,自定义类型调用它的赋值。stake类涉及空间开辟的情况,需要自己实现完成深层次拷贝,介绍赋值运算符之前,我们先要了解c++中的运算符重载。

c++运算符重载

对于+ - * /这样运算符内置类型可以直接使用,但是自定义类型不可以直接使用

int a = 1;

int b = 2;

c = a+ b;

但是 Date d1 Date d2

d1 == d2这样就不可以直接使用。

对于内置类型来说,编译器知道其类型的含义就可以进行比较,直接就转换成指令了

但是对于自定义类型来说,编译器不知道要按照什么样的规则来进行比较。

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

函数名字为:关键字operator后面接需要重载的运算符符号。

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

注意:

不能通过连接其他符号来创建新的操作符:比如operator@

重载操作符必须有一个类类型参数

用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this

那么日期类的比较就可以这样来写,关于日期的比较不像自定义类型那些谁大就大,而是有一个比较逻辑,编译器是不能确定我们自己定义的类的比较逻辑的,无法统一所以就无法比较。

bool operator>(const Date& d1, const Date& d2)
{if (d1._year > d2._year){return true;}if (d1._year == d2._year && d1._mouth > d2._mouth){return true;}if (d1._year == d2._year && d1._mouth == d2._mouth && d1._day > d2._day){return true;}return false;}

此时的c++可以支持这样写;

 此时的使用就和我们的内置类型使用运算符感觉没有差异了。

当我们将这个函数的实现放在类里面就会报错:

因为还有一个隐藏的this指针,参数要和操作数匹配,所以这样修改:

bool operator==( const Date& d2)
{return _year == d2._year&& _mouth == d2._mouth&& _day == d2._day;
}
bool operator>( const Date& d2)
{if (_year > d2._year){return true;}if (_year == d2._year && _mouth > d2._mouth){return true;}if (_year == d2._year && _mouth == d2._mouth && _day > d2._day){return true;}return false;}

 调用时这样调用:

d1.operator>(d2);

可以重载那些运算符,主要看这个运算有没有意义,有意义就可以实现,没有意义就不要实现。

赋值:赋值运算符的重载:

两个已经存在的对象进行拷贝。而拷贝构造是一个已经存在的对象去拷贝初始化另外一个对象。

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

赋值函数是一个默认构造函数,operate= 我们不写编译器会默认生成一个。行为跟拷贝构造类型,对内置类型完成值拷贝,对自定义类型完成他的赋值。date可以不写,myqueque1不写,栈要写,完成深度拷贝。

以上就是c++中的默认成员函数以及支持的默认行为。下一篇文章将介绍c++中的运算符重载

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

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

相关文章

基于AIOHTTP、Websocket和Vue3一步步实现web部署平台,无延迟控制台输出,接近原生SSH连接

背景&#xff1a;笔者是一名Javaer&#xff0c;但是最近因为某些原因迷上了Python和它的Asyncio&#xff0c;至于什么原因&#xff1f;请往下看。在着迷”犯浑“的过程中&#xff0c;也接触到了一些高并发高性能的组件&#xff0c;通过简单的学习和了解&#xff0c;aiohttp这个…

【鸿蒙HarmonyOS Next实战开发】lottie动画库

简介 lottie是一个适用于OpenHarmony和HarmonyOS的动画库&#xff0c;它可以解析Adobe After Effects软件通过Bodymovin插件导出的json格式的动画&#xff0c;并在移动设备上进行本地渲染。 下载安裝 ohpm install ohos/lottieOpenHarmony ohpm 环境配置等更多内容&#xff0c…

UE_C++ —— UObject Instance Creation

目录 一&#xff0c;UObject Instance Creation NewObject NewNamedObject ConstructObject Object Flags 二&#xff0c;Unreal Object Handling Automatic Property Initialization Automatic Updating of References Serialization Updating of Property Values …

PHP本地商家卡券管理系统

本地商家卡券管理系统 —— 引领智慧消费新时代 本地商家卡券管理系统&#xff0c;是基于ThinkPHPUni-appuView尖端技术匠心打造的一款微信小程序&#xff0c;它彻底颠覆了传统优惠方式&#xff0c;开创了多商家联合发行优惠卡、折扣券的全新模式&#xff0c;发卡类型灵活多变…

什么是HTTP Error 429以及如何修复

为了有效管理服务器资源并确保所有用户都可以访问&#xff0c;主机提供商一般都会对主机的请求发送速度上做限制&#xff0c;一旦用户在规定时间内向服务器发送的请求超过了允许的限额&#xff0c;就可能会出现429错误。 例如&#xff0c;一个API允许每个用户每小时发送100个请…

无人机不等同轴旋翼架构设计应用探究

“结果显示&#xff0c;对于不等组合&#xff0c;用户应将较小的螺旋桨置于上游以提高能效&#xff0c;但若追求最大推力&#xff0c;则两个相等的螺旋桨更为理想。” 在近期的研究《不等同轴旋翼性能特性探究》中&#xff0c;Max Miles和Stephen D. Prior博士深入探讨了不同螺…

节目选择器安卓软件编写(针对老年人)

文章目录 需求来源软件界面演示效果源码获取 对爬虫、逆向感兴趣的同学可以查看文章&#xff0c;一对一小班教学&#xff1a;https://blog.csdn.net/weixin_35770067/article/details/142514698 需求来源 由于现在的视频软件过于复杂&#xff0c;某客户想开发一个针对老年人、…

Vue的简单入门 一

声明&#xff1a;本版块根据B站学习&#xff0c;创建的是vue3项目&#xff0c;用的是vue2语法风格&#xff0c;仅供初学者学习。 目录 一、Vue项目的创建 1.已安装15.0或更高版本的Node.js 2.创建项目 二、 简单认识目录结构 三、模块语法中的指令 1.v-html 1.文本插值…

【动手学强化学习】03马尔可夫决策过程

马尔可夫决策过程始终贯穿强化学习&#xff0c;要学好强化学习&#xff0c;必须掌握马尔可夫决策过程的基础知识。与多臂老虎机不同&#xff0c;马尔可夫决策过程包含状态信息以及状态转移机制。 马尔可夫过程 当且仅当某时刻的状态只取决于上个时刻的状态时&#xff0c;一个…

RabbitMQ学习—day2—安装

目录 普通Linux安装 安装RabbitMQ 1、下载 2、安装 3. Web管理界面及授权操作 Docker 安装 强力推荐学docker&#xff0c;使用docker安装 普通Linux安装 安装RabbitMQ 1、下载 官网下载地址&#xff1a;https://www.rabbitmq.com/download.html(opens new window) 这…

SQL Server的安装和简单使用

目录 一、SQL Server 1.1、简介 1.2、安装包 二、安装SQL Server 2.1、双击安装包 2.2、选择自己想要安装的位置 2.3、点击安装 2.4、安装完成之后会出现以下页面&#xff0c;按照序号依次点击 2.5、不用管密钥&#xff0c;点击下一步 2.6、选择【我接受】 2.7、是否…

前缀和(Prefix Sum)算法笔记C++

前缀和(Prefix Sum)算法介绍 前缀和是一种预处理技术, 用于快速计算数组中任意子区间的元素之和. 它通过一次遍历创建一个辅助数组来存储从数组起始位置到当前索引位置所有元素的累加和, 从而使得后续查询操作的时间复杂度降低至 O ( 1 ) O(1) O(1). 定义 对于给定的数组 n…

ffmpeg学习:ubuntu下编译Android版ffmpeg-kit

文章目录 前言一. 配置环境1.1 虚拟机版本1.2 安装Android环境1.2.1 Android SDK安装1.2.2 Android NDK安装 1.3 编译前的准备工作1.3.1 libtasn1-1安装1.3.2 meson安装1.3.3 harfbuzz下载 二. 编译ffmpeg-kit三. 总结 前言 ffmpeg-kit是一款跨多个平台的&#xff0c;用于在应…

Vue 中报错 TypeError: crypto$2.getRandomValues is not a function

问题 在新建的项目中&#xff0c;使用的是 npm init vue 创建项目后&#xff0c;执行命令 npm i &#xff0c;然后去 npm run dev 这个时候报错 TypeError: crypto$2.getRandomValues is not a function 起初是以为搞错了&#xff0c;然后再删掉 node_modules 和 package-lo…

‌OpenAI GPT-4.5技术详解与未来展望

一、GPT-4.5的技术突破‌ OpenAI在人工智能领域的持续创新再次引领了技术潮流。近期,OpenAI内部已经成功实现了GPT-4.5的开发,这一版本相较于前代在多个方面实现了显著的技术突破‌。 GPT-4.5在算法优化和数据处理上进行了多项创新,使得模型在对自然语言的理解上,尤其是在…

某生产制造企业积分制考核信息化项目成功案例纪实

某生产制造企业积分制考核信息化项目成功案例纪实 ——打破“大锅饭”“平均主义”问题&#xff0c;持续激励员工&#xff0c;调动员工积极性 【客户行业】生产制造行业 【问题类型】薪酬体系优化 【客户背景】 某大型钢铁集团公司是一个集科工贸、产供销于一体的国有生产…

「软件设计模式」适配器模式(Adapter)

软件设计模式深度解析&#xff1a;适配器模式&#xff08;Adapter&#xff09;&#xff08;C实现&#xff09; 一、模式概述 适配器模式&#xff08;Adapter Pattern&#xff09;是结构型设计模式中的"接口转换器"&#xff0c;它像现实世界中的电源适配器一样&#…

Windows 下安装 Python 和 Nodejs

Windows 下安装 Python 和 Nodejs 1. Windows 下安装 Python2. Windows 下安装 Nodejs 1. Windows 下安装 Python 访问 https://www.python.org/downloads/windows/&#xff0c;下载想使用的版本&#xff0c; 2. Windows 下安装 Nodejs 访问 https://nodejs.org/en/download&…

【算法与数据结构】并查集详解+题目

目录 一&#xff0c;什么是并查集 二&#xff0c;并查集的结构 三&#xff0c;并查集的代码实现 1&#xff0c;并查集的大致结构和初始化 2&#xff0c;find操作 3&#xff0c;Union操作 4&#xff0c;优化 小结&#xff1a; 四&#xff0c;并查集的应用场景 省份…

Unity学习part1

课程为b站【Unity教程】零基础带你从小白到超神 1、脚本执行顺序 unity的脚本执行顺序不像blender的修改器那样按顺序执行&#xff0c;而是系统默认给配置一个值&#xff0c;值越小&#xff0c;执行顺序越靠前&#xff08;注意&#xff0c;这个顺序是全局生效的&#xff09; …