C++语言·类和对象(下)

news2024/11/15 11:46:42

1. 初始化列表

        我们回忆上节写的MyQueue类,其中有两个栈类和一个int类型,栈类因为其特殊性,要开空间,所以我们必须手搓Stack类的构造函数。但是正常来说MyQueue自动生成的构造函数会调用自定义类型的默认构造函数,也就是说我们不必再手搓MyQueue类的构造函数了,编译器自动生成的就好用。

        但是此时问题来了,默认构造函数简单点理解就是不用传参就能调用的构造函数,那么如果我们手搓的Stack类没有默认构造函数,那么MyQueue就不能自动生成构造函数了,因为它没得调了,就像这样。

                

        光标圈出来的那一行中,我们没有给构造函数搞缺省参数,所以它必须传参才能调用,也就是说此时Stack类没有了默认构造函数,那么现在编译器是处在报错的状态,那我们如何通过写MyQueue的构造函数来解决这一问题。

        此时就要说到初始化列表的概念了,初始化列表本质上可以理解成每个对象中成员定义的地方,所有成员你可以在初始化列表初始化,也可以在函数体内部初始化。

        具体写法就是:以一个冒号开始,接着是一个以逗号分开的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。

                        

        ps: 这里面size其实应该前面有个下划线的,但是我忘记写了,大家凑活着看吧

        _pushst 和 _popst 的初始化必须要在函数体外面写,因为他俩进了函数体就没法初始化了呀,不过size初始化可以写到函数体里头,当写在函数体里头的时候有必须要用赋值的方案进行初始化了。

        虽说类中的所有成员都可以在初始化列表和函数体内初始化,但是有三类成员必须在初始化列表中初始化:1. 引用        2. const成员        3. 没有默认构造的自定义类型成员

        我们知道如果在定义成员变量的时候赋值,我们管这个行为叫给成员变量缺省值,这个缺省值的含义就是为了给初始化列表的缺省值。因为初始化列表不管你有没有显式写,编译器都会自动给每个成员都走一遍,自定义类型的成员会调用其默认构造函数,内置类型就不做操作(除非有缺省值,那么编译器的行为相当于自动在括号中写那个缺省值)。这就解释了上面Stack没有默认构造函数MyQueue就不能自动生成构造函数。

        既然是缺省值,我们在显式写初始化列表的时候就可以更改,同时默认构造函数那个赋的不也是缺省值嘛,因此也可以更改。

                ​​​​​​​        

        这里强调一下,缺省值是可以给表达式的,比如我们可以给一个指针成员一个malloc缺省值。

        那么函数体中的语句的优先顺序比初始化列表还要高,也就是说初始化列表中已经初始化好的成员我们还可以在函数体中进一步修改。具体优先顺序就是: 缺省值 < 初始化列表 < 函数体

        成员变量在类中声明次序就是其在初始化列表中的初始化顺序,初始化顺序与其在初始化列表中的先后次序无关。我们看下面这段代码的输出就懂了

        ​​​​​​​        

        因为是先初始化的_a2所以输出的不是两个1,而是1和随机值。

        最后在实践中,尽可能使用初始化列表进行初始化,不方便再用函数体初始化。

2. 对象赋值时的隐式类型转换

        我们看下面这一段代码

                        ​​​​​​​​​​​​​​

        我们知道aa1通过构造函数赋值,aa2通过拷贝构造赋值,那aa3是怎么回事?raa又是怎么回事?

        aa3这里其实产生了隐式类型转换,由内置类型转换为了自定义类型。首先3先构造一个A类型的临时对象,再用这个临时对象拷贝构造aa3。所以说单参数构造函数可以支持这种直接赋内置类型的操作,它会产生隐式类型转换。现在我们在看打印出来的结果,当 3 赋给 aa3 的时候只调用了构造函数,这是因为编译器遇到 连续构造+拷贝构造 的组合时会优化成直接构造.

        raa的本意是取引用,首先 3 先隐式转换出来一个临时对象,为了权限不放大,所以要给const,之后raa引用3转换出来的这个新的临时对象。所以只有 3 隐式转换的时候出现了构造函数,没有拷贝构造出现。

        这个隐式类型转换的意义就是在于能够简化我们的代码量,有时还能缩减运行效率,直接赋一个内置变量,让编译器自己去类型转换。比如说我们想往栈里头压这个A类对象:

        ​​​​​​​        ​​​​​​​        

        我们可以选择先构造一个对象,然后再给到压栈。或者直接不写构造了,直接压1,让编译器自己类型转换去,这不就方便多了。

        上面是单参数的隐式类型转换,那多参数的写法是这样的:

        ​​​​​​​        ​​​​​​​        

        给值的时候用花括号括起来,写不写等于号都行,但是推荐写上等于号,这样和普通括号有点区别。

2.1 explicit关键字

        如果不想让构造能发生隐式类型转换,就在构造函数前面加上explicit关键字就行,单参数和多参数通用的。

        ​​​​​​​        ​​​​​​​        ​​​​​​​​​​​​​​

        aaa1因为隐式类型转换被拒所以无法构造了,那个aaa2没报错是因为编译器在这里把花括号当括号处理了。我们看aaa掉成员函数时的情况,传对象的时候就通过了,但是尝试传内置类型的时候隐式类型转换被拒绝了,因此编译报错。

3. static 静态成员

        声明为static的类的成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员函数一定要在类外进行初始化。

        静态成员有如下特性:

        1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。

        2. 静态成员变量必须定义在类外定义,定义时不添加static关键字,类中只是声明

        3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员访问

        4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

        5. 静态成员也是类的成员,受 public,protected,private 访问限定符的限制

        ​​​​​​​        ​​​​​​​        

        我们看上面一段代码。

        _scount是一个静态成员变量,这里我将它开放成了public,否则无法在类外访问。它存储于静态区,并属于所有的A类和其实例(对象),也就是说它可以直接通过类 A::_scount 访问,同时从任何地方访问(通过类访问,或通过对象访问)并改变其值的操作,都会直接改变其在静态区存储的值,也就是说任何依次改变都会影响A类以及其下所有实例中的_scount值,这就是所谓的静态成员变量属于所有类对象。同时因为它存储于静态区,所以在实例化类的时候静态成员变量同成员函数一样,不占用对象的空间。

        _scount在类中声明的时候不能给缺省值,给了就报错,因为给缺省值是为了在初始化列表中初始化,但是_scount不走初始化列表,它是静态变量,因此必须在类外定义

        那么这个静态成员变量可以用来干嘛呢,举个例子,它可以实时检测该类存在几个实例化对象,因为创建对象无非两种方法,构造和拷贝构造,因此我显式写每执行依次这两种构造就++_scount ,对象销毁析构的时候再 --_scount。最终打印出来的效果还是挺明显的。

        我们再看下一段代码,讲解一下静态成员函数

        ​​​​​​​        ​​​​​​​        ​​​​​​​

        这里我将_scount设置为private状态了,那么此时就不能再类外访问它了,也就是说无法这样 A::_scount 访问了,当然这也比较符合我们写成员变量的习惯,就是把所有成员变量都设置成private状态加以保护。那此时我们就要通过静态成员函数访问。static修饰变量和修饰函数的意义完全不一样,修饰变量时改变了变量的声明周期,修饰函数时改变了它的链接属性。那么静态成员函数的特性就是没有this指针,也就是说静态成员函数只能访问静态成员变量。

        那么使用的时候跟静态成员函数很像,既可以通过类访问,也可以通过对象访问得到_scount的值

4. 友元

        友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

        友元分为友元函数和友元类:

4.1 友元函数

        在上节重载流插入和流提取的时候,我们为了保证传参顺序符合习惯,因此将流插入和流提取的重载函数写在了类外,从而引发了无法访问private成员变量,当时我们给出的解决方案是写Get函数,取消private状态肯定是不可取的,要不我们还封装个什么。

        那么本节我们可以通过友元函数的设置来打破针对某一个函数的封装,其实上节最后实现日期类的时候我就已经搞友元了不知道大家注意到了没有。

                

        这里我把日期类简化了一下,就保留了流插入和流提取的内容,在类刚开始那两行用friend修饰的代码就是友元函数的修饰,用一个friend关键字修饰一个外部函数的声明,就能使这个外部函数访问类内部的private信息了。

4.2 友元类

        跟友元函数类似,在一个类中声明另一个类是友元的,那么就可以在另一个类中访问这个类的私有信息。形象一点来说就是:你是我的朋友,那么你就可以知道我的一些私有信息。

        ​​​​​​​        ​​​​​​​        

        B中可以访问A中的私有成员,但是A不能访问B的成员。

        最后还要说一下友元不能传递,就比如C是B的友元,B是A的友元,但是C不是A的友元。也就是说,A不能访问C的私有成员。

5. 内部类

        如果一个类定义在另一个类的内部,这个内部的类就叫做另一个类的内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的私有成员。外部类对内部类没有任何优越的访问权限。

        内部类天生是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员,但是外部类不是内部类的友元

        说白了就是外部类和内部类就是两个几乎完全平行的两个类,唯一不同的就是内部类要受到类域的限制,使用时必须在外部类类域下使用,同时内部类天生是外部类的友元。

        ​​​​​​​        ​​​​​​​        ​​​​​​​

6. 匿名对象

        匿名对象就是在实例化的时候不给名字,它的生命周期只在当前这一行,即用即销毁

        ​​​​​​​        ​​​​​​​        

7. 拷贝对象时的一些编译器优化

        因为我的编译器是vs2022优化开的太大了,没办法演示,就简单描述一下了。优化的话如果在Debug版本下,一定是在一行代码中进行优化的。但是如果开成release版本就有可能产生跨行优化。我下面讲的都是在Debug版本下的编译器优化方案。

        隐式类型:连续构造+拷贝构造 -> 优化成直接构造

        传值返回:拷贝构造+拷贝构造 -> 优化为一个拷贝构造,这里有一个值得注意的点,就是拷贝构造+赋值运算重载是无法优化的

        

        

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

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

相关文章

C语言 | Leetcode C语言题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; int firstMissingPositive(int* nums, int numsSize) {for (int i 0; i < numsSize; i) {while (nums[i] > 0 && nums[i] < numsSize &&nums[nums[i] - 1] ! nums[i]) {int t nums[nums[i] - 1];nums[nums[i] -…

SQLite运行时可加载扩展(三十五)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite轻量级会话扩展&#xff08;三十四&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 1. 概述 SQLite 能够在运行时加载扩展&#xff08;包括新的应用程序定义的 SQL 函数、整理序列、虚拟表和 VFS&#xff09…

NineData正式将SQL开发正式升级为数据库DevOps

NineData SQL 开发早期主要提供 SQL 窗口&#xff08;IDE&#xff09;功能&#xff0c;产品经过将近两年时间的打磨&#xff0c;新增了大量的企业级功能&#xff0c;时至今日已经服务了上万开发者&#xff0c;覆盖了数据库设计、开发、测试、变更等生命周期的功能。 为了让企业…

C++相关概念和易错语法(7)(初始化列表、隐式类型转换、友元)

1.初始化列表 初始化列表是集成在构造函数里面的&#xff0c;对象在创建的时候一定会调用构造函数&#xff08;就算不显式定义&#xff0c;也会自动生成并调用&#xff09;。初始化列表就是这些对象的成员变量在创建的时候初始化的地方。 下面是使用的例子&#xff0c;可以先…

接口测试相关

接口测试&#xff0c;接口 接口是数据交互的入口和出口 接口是一套规范和标准 统一设计标准 前后端相对独立 扩展型灵活 接口文档。 接口测试 接口测试环境&#xff0c;运行程序&#xff0c;自己搭建环境 接口测试插件 谷歌postman 火狐 restclient java测试工具为j…

Docker - 入门基础

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 入门基础 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-basic.html Docker架构 Docker 使用的是客户端-服务端&#xff08;C/S&#xff09;架构模式&#xff0c;使用远程 API 来管理和创建…

滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南

滚动条是用户界面中的图形化组件&#xff0c;用于指示和控制内容区域的可滚动范围。当元素内容超出其视窗边界时&#xff0c;滚动条提供可视化线索&#xff0c;并允许用户通过鼠标滚轮、触屏滑动或直接拖动滑块来浏览未显示部分&#xff0c;实现内容的上下或左右滚动。它在保持…

NASA数据集——TANSO-FTS 运行前 11 年收集的测量数据中得出二氧化碳(CO2)干空气摩尔分数(XCO2)的估计值

ACOS GOSAT/TANSO-FTS Level 2 bias-corrected XCO2 and other select fields from the full-physics retrieval aggregated as daily files V7.3 (ACOS_L2_Lite_FP) at GES DISC 简介 ACOS Lite 文件包含经过偏差校正的 XCO2 以及其他选定字段的每日汇总文件。ACOS 2 级标准…

Unity ECS

一&#xff1a;前言 ECS与OOP不同&#xff0c;ECS是组合编程&#xff0c;而OOP的理念是继承 E表示Entity&#xff0c;每个Entity都是一个有唯一id的实体。C表示Component&#xff0c;内部只有属性&#xff0c;例如位置、速度、生命值等。S表示System&#xff0c;驱动实体的行为…

js 函数节流和函数防抖及区别详解

文章目录 1. 前言2. 函数节流3. 函数防抖4. 总结 1. 前言 浏览器中总是有一些操作非常耗费性能。所以就有了函数节流和函数防抖来提高浏览器性能。 函数节流&#xff1a;频繁触发一个事件时候&#xff0c;每隔一段时间&#xff0c;函数只会执行一次。 函数防抖&#xff1a;当触…

js 遍历数据结构,使不符合条件的全部删除

js 遍历数据结构&#xff0c;使不符合条件的全部删除 let newSourceJSON.parse(JSON.stringify(state.treeData))state.expandedKeys[]checkedKeys.map((item:any)>{loop(newSource,{jsonPath:item.split(&)[1]},state.expandedKeys)})function removeUnwantedNodes(tre…

react 项目路由配置(react-router-dom 版本 v6.3、v6.4)

根据 react-router-dom 的版本&#xff0c;有不同的方式 一、react-router-dom v6.3 用到的主要 api: BrowserRouteruseRoutesOutlet 下面是详细步骤&#xff1a; 1、index.js BrowserRouter 用来实现 单页的客户端路由使用 BrowserRouter 包裹 App放在 顶级 位置&#x…

香港服务器_免备案服务器有哪些正规的?企业、建站方向

香港服务器&#xff0c;是最受欢迎的外贸、企业建站服务器&#xff0c;在个人建站领域&#xff0c;香港服务器、香港虚拟主机都是首选的网站服务器托管方案&#xff0c;不仅其具备免备案的特点&#xff0c;而且国内外地区访问速度都很快。那么&#xff0c;现今2024年个人和企业…

使用Cesium ion将 Sketchfab 3D 模型添加到您的GIS应用中

您现在可以将 Sketchfab 中的 3D 模型导入 Cesium ion 中以创建 3D 块&#xff0c;从而更轻松地为地理空间体验创建上下文和内容。 Sketchfab 是 Epic Games 的一部分&#xff0c;也是使用最广泛的 3D 资产市场之一。自 2012 年推出以来&#xff0c;已有超过 1000 万用户使用 …

《设计模式之美》- 总结

《设计模式之美》- 总结 第一章 概述 1.1 为什么学习代码设计 编写高质量的代码应对复杂代码的开发程序员的基本功职业发展的必备技能 1.2 如何评价代码的质量 1.2.1 可维护性 可维护性代码的特性&#xff1a;代码简洁、可读性好、可扩展性好代码分层结构清晰、模块化程度…

Spring Boot + Thymeleaf 实现的任务发布网站

角色&#xff1a; 管理员雇主雇员 功能 雇主&#xff1a;登录、注册、发布任务、选择中标雇员、评价雇员雇员&#xff1a;登录、注册、查看任务列表、投标任务、收藏任务、完成任务管理员、登录、任务管理、雇主管理、雇员管理 部分功能截图 部署 导入数据库…

【剪映专业版】13快速为视频配好音:清晰、无噪声、对齐

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 使用场景&#xff1a;视频无声音或者视频有声音但是需要更改声音 时间指示器在哪里&#xff0c;就从哪里开始 红色按钮&#xff1a;开始录音 声音波纹&#xff1a;蓝色最佳&#xff0c;黄色或红色声音太大&#xff0c;…

【Django】学习笔记

文章目录 [toc]MVC与MTVMVC设计模式MTV设计模式 Django下载Django工程创建与运行创建工程运行工程 子应用创建与注册安装创建子应用注册安装子应用 数据模型ORM框架模型迁移 Admin站点修改语言和时区设置管理员账号密码模型注册显示对象名称模型显示中文App显示中文 视图函数与…

CCIE-16-PIM

目录 实验条件网络拓朴实验环境实验目的 开始实验实验1&#xff1a;PIM-DM配置PIM域中的路由&#xff0c;开启PIM-DM组播路由功能&#xff0c;验证组播情况 实验2&#xff1a;PIM-SM&#xff08;静态RP&#xff09;配置PIM域中的路由&#xff0c;开启PIM-SM组播路由功能&#x…