C++:类和对象(三)

1.深入了解构造函数

1.1初始化列表

引入:我们首先要知道,在类中我们是声明变量,在实例化的时候才是开空间,在调用构造函数的时候又分两段,一段是定义(所有的成员变量进行定义),一段是赋值(构造函数体内,也就是{}包含的地方)。

而我们所说的初始化列表就是可以实现在构造函数的定义区进行赋值

补充:说开空间也不是很准确。准确来说,我们在开空间的时候是编译器预估好函数大概需要占用的空间去开辟,然后我们的对象再从已经开了的空间中占用一定的空间,如果不够我们才会接着开。

(1)格式:初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。而这种方式也叫显式实现初始化列表


(2)重点:每个成员变量在初始化列表中只能出现⼀次语法理解上初始化列表可以认为是每个成员变量定义的地⽅。

这也是我在引入的时候说的,初始化列表的位置就是所有成员变量集体定义的地方,若用了初始化列表就相当于可以顺便初始化了。


(3)引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错。

1.引用:因为引用必须在定义的时候初始化(语法要求),所以我们必须在初始化列表这个所有变量定义的地方对它初始化。

就像这里,我们没有在初始化列表给引用c初始化,所以报错了。

我们没实现引用的初始化列表的时候相当于下面代码所示

也就是说没有实现初始化列表的时候,引用他也会自动在这里定义,但是因为他自动的定义没有指定引用对象,所以会报错。

2.const成员变量:和引用类似,const成员变量也需要在定义的时候初始化。这是因为语法规定const变量的值只有一次改变的机会,就是定义的时候。那如果我们在定义的时候没有给它值,他就没有意义了,所以报错。

这里我们的const变量_a没有在初始化列表实现给初始值,所以报错

3.没有默认构造的类类型变量:没有默认构造的类类型也就相当于它的构造函数必须传参数。如果不实现类类型初始化列表,由于编译器自动实现的定义不会传参数,所以调用构造失败,就会报错

这里我们就是因为没有实现初始化列表传参去调用构造函数,所以报错了


(4)C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显式在初始化列表初始化的成员使⽤的。也叫隐式实现初始化列表

格式就是在声明后面利用赋值符号给值(只能给一个缺省值)

其实就和缺省参数一个道理,我们在这里给了缺省值,相当于编译器就能在定义的时候回来获取这个值去进行初始化。


(5)尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表(因为所有成员变量都会在初始化列表的地方定义)。不过要注意要么我们全部显式实现,要么我们全部隐式实现,不要混着用。

(6)初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆关。建议声明顺序和初始化列表顺序保持⼀致
这里我们的预期是_a = 1,然后_a又给_c赋值,所以_c = 1。
那么最后输出的答案应该是11。
这里为什么_c是随机值?
因为我们初始化的顺序并不是按照初始化列表的顺序,而是按照声明的顺序。
声明的顺序=开空间顺序=初始化顺序
所以我们先给_c初始化,_c=_a,可是_a此时还没有初始化,还是个随机值,所以_a将随机值就给了_c。然后我们再给_a初始化,由于我们传了一个参数1,所以_a的值就是1.

(7)总结:

我们总共有三条初始化路线:

1.显式实现初始化列表:就按照显式的来初始化

2.隐式实现初始化列表:如果写了显式就按照显式,没有就按照隐式

3.没实现初始化列表:按照我们写的构造函数体走

总共有两大种属性:

1.必须用初始化列表初始化的属性:const修饰的变量,引用,需要显式传参的自定义类型

2.可以不用初始化列表初始化的属性:其他的属性

2.类型转换

(1)C++⽀持内置类型隐式类型转换为类类型对象,但是需要有相关内置类型为参数的构造函数。

其实就是需要我们去写一个构造函数,用内置类型构造出一个自定义类型的临时对象,然后通过拷贝构造函数,把临时对象的值给新创建的对象初始化。


(2)类类型的对象之间也可以隐式转换,需要相应的构造函数支持


(3)传递多参数的类型转换

我们多参数的传递方式就是使用花括号去包含参数。


注意:构造函数前⾯加explicit就不再⽀持隐式类型转换。
这时就说明这个构造不支持隐式类型转换了

(4)隐式类型转换的实际过程

首先,内置类型通过构造函数创建了一个新的临时对象,然后这个临时对象通过拷贝构造函数给要类型转换成的对象初始化

3.友元函数

友元提供了⼀种突破类访问限定符封装的⽅式(也就是可以访问类中的私有成员,但不是类的成员函数。

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


(1)格式:在函数声明或者类声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。

这里我们声明了一个友元类和一个友元函数,他们的函数/类声明前面加了个friend。并且都在类a里面

(2)⼀个函数可以是多个类的友元函数。
也就类似于,一个人可以获得不同的国家的签证,然后去进入这些国家。一个函数可以是多个类的友元函数,然后访问他们的私有成员
(3)友元类的关系是单向的,友元类关系不能传递。
你获得了他的身份令牌,不代表他获得了你的身份令牌(单向)
你获得的身份令牌不能传递给其他人(不能传递)
友元会增加耦合度,破坏了封装,所以友元不宜多⽤。

总结:所以与其说友元函数是当好朋友,不如说是获取了身份令牌更合适,有了这个类给你认证了身份令牌,你就可以访问他的私有成员了。

但是他没在你的类里面声明为友元函数,也就是没有获取你的身份令牌,所以无法访问你的私有成员

4.static成员

(1)⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。


(2)突破类域就可以访问静态成员,可以通过类名::静态成员(只有静态类型可以这样用) 或者 对象.静态成员(和访问类中的普通属性一样的访问方法) 来访问静态成员变量和静态成员函数。

疑问:为什么这里我们无法访问static修饰的 _c呢?

这是因为静态成员也是类的成员,受public、protected、private 访问限定符的限制。如果我们把它放到public部分就可以访问了

接下来我们看看静态变量特有的访问方式

这种类名::的访问方式是static类型的变量特有的,类中的其他成员变量都不能这样访问

这是因为其他成员变量是属于每个不同的对象的,所以一定要利用对应的对象来调用。

而我们的static类型的成员变量是一个类中所有对象公用的,并不属于某个对象,不存放在对象中,存放在静态区。所以可以用类名直接调用


(3)静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表。

这里报错就是因为static变量不会走初始化列表,所以给初始化列表传值的缺省值就不能用在static变量上
⽤static修饰的成员函数,称之为静态成员函数

不过和其他成员函数不同的是,静态成员函数没有this指针。这是因为静态成员函数和对象的实例化无关,他不属于任何一个对象,所以不需要默认有一个this指针

(1)也正是因为没有默认一个this指针,所以静态成员函数不能访问非静态的成员,之所以可以访问静态的,是因为静态的不属于任何一个对象,可以脱离this去访问。

这里我们看到可以直接访问静态成员变量
(2)⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
因为静态成员函数和变量是该类的所有对象共用的
这里我们就是普通的成员函数,可以直接调用静态成员函数或者变量。

接下来我们看看一道题


1.限制了循环语句:所以我们不能用循环的方法来做

2.限制了判断语句:所以我们不能用递归做

3.限制了乘除法:所以不能用公式来做

现在我们只剩一种方法:利用n个数据的构造函数,每次进入构造函数就进行一次累加运算.

要实现这个有个前提是我们要有静态成员变量


第一步:写一个专门负责运算的类(Sum类),并设置其成员变量。

其中一个成员变量是sum,负责保存总和。

还有一个i,负责根据调用次数改变自己的值。

由于n>0,所以我们设置i为1

且因为他们生命周期要求是整个程序的生命周期,所以设置成静态成员变量。

第二步:在Sum构造函数中写运算逻辑

因为n=1的时候我们的总和是1,所以我们先进行运算,然后再让i++

第三步:写返回Sum的函数

之所以写成静态的,是因为我们的返回函数和具体的某个对象没有关系,我们这里只需要返回一次全体的就行。

第四步:调用

先写一个变长数组,实现n次调用构造函数(自定义类型的数组的每个元素都会依次调用构造函数初始化,如果要实现初始化也可以利用静态变量)。然后利用GetSum函数返回总和的值

构造函数的调用顺序为:c a b d

析构函数的调用顺序为; b a d c

这是因为a和b都是局部变量,生命周期局限于局部。而c是全局变量,d是静态变量,他们的生命周期都是整个程序的生命周期。所以a和b一定比c和d先析构。

又因为b比a后定义,所以b比a先析构

又因为d比c后定义,所以d比c先析构

5.匿名对象

(1)⽤ 类型(实参) 定义出来的对象叫做匿名对象,之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象

我们看到匿名对象把对象名给省略掉了,不过如果不传参是需要写个空的小括号上去的


(2)匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

通过调试我们看到,匿名对象的构造函数调用完,他那一行一结束析构函数就调用了,说明它的生命周期确实只有一行。

思考:那我们有没有办法延长他的生命周期呢?

只要用加上const修饰的引用就可以

我们看到这里虽然已经走完那一行代码了,但是我们没有调用析构函数去销毁匿名对象,这说明它的生命周期不再是一行了。而是和引用的生命周期一致。


(3)匿名对象的两个应用场景

1.做缺省参数

这里我们如果要给对象缺省参数,有两个方法

方法一:传一个内置类型,然后进行类型转换。

但是这个方法比较麻烦,因为我们还要单独写一个构造函数去帮他类型转换,并且看起来并不直观

方法二:给他一个匿名对象,看起来就更加直观了

2.临时调用成员函数时可以减少一行代码

不过这个调用的函数是有要求的,他最好是临时需要使用一次就行的那种函数,比如输入输出之类的。

6.内部类

(1)如果⼀个类定义在另⼀个类的内部,这个类就叫做内部类。内部类是⼀个独⽴的类,跟定义在全局相⽐,他只是受外部类类域限制和访问限定符限制,而外部类定义的对象中不包含内部类。

这里我们的inner类就是test类的内部类。注意内部类和外部类之间不是君臣关系,内部类并不属于外部类,只是相当于内部类的类域处于外部类的类域之中而已

1.受外部类类域限制

我们需要在inner类前面使用域作用符去突破外部类类域的限制。(放在public才能实例化)

2.访问限定符限制

我现在把inner类放在了private部分,接下来我们看看是否还能初始化

报错了,这是因为inner类被test类中private给修饰了,也就是我们无法使用这个类。

有一种方法可以让我们在外部类中访问内部类的成员函数

方法:在外部类成员函数中实例化内部类对象

相当于在main函数中实例化一个类,然后访问这个对象的public函数

反思:实际上我们在外部类的成员函数进行内部类的实例化这个过程,类似于我们在main函数中实例化一个普通的类的过程。


(2)内部类默认是外部类的友元类。

也就是说内部类的成员函数可以访问外部类的私有成员。

(3)内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地⽅都⽤不了。
比如我们前面的sum类,就是写出来给solution用的,那我们就可以把sum写成solution的内部类

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

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

相关文章

北京申请中级职称流程(2024年)

想找个完整详细点的申请流程资料真不容易,做个分享送给需要的人吧。 不清楚为什么说文章过度宣传,把链接和页面去掉了,网上自己找一下。 最好用windows自带的EDGE浏览器打开申请网站,只有在开始申请的时间内才可以进行网上申报&…

好用的js组件库

lodash https://www.lodashjs.com/https://www.lodashjs.com/ uuid 用于生成随机数,常用于生成id标识 GitHub - uuidjs/uuid: Generate RFC-compliant UUIDs in JavaScripthttps://github.com/uuidjs/uuid dayjs 常用于时间的处理 安装 | Day.js中文网 (fenxi…

php:使用socket函数创建WebSocket服务

一、前言 闲来无事&#xff0c;最近捣鼓了下websocket&#xff0c;但是不希望安装第三方类库&#xff0c;所以打算用socket基础函数创建个服务。 二、构建websocket服务端 <?phpclass SocketService {// 默认的监听地址和端口private $address 0.0.0.0;private $port 8…

Tailscale 自建 Derp 中转服务器(全程无 Docker + 无域名纯 IP 版本)

文章目录 整体大纲目的&#xff1a;为什么要建立 Derp 中转服务器云服务器安装 DerpDerp 中转服务器介绍安装 Go 环境通过 Go 安装 Derp处理证书文件自签一个域名给 Derp验证 Derp 是否启动 云服务器安装并登录 Tailscale第一种组合&#xff1a; 官方 Tailscale 账号 自己的 D…

shell--第一次作业

1.接收用户部署的服务名称 # 脚本入口 read -p "请输入要部署的服务名称&#xff1a;" service_name 2.判断服务是否安装 # 判断服务是否安装 if rpm -q "$service_name" &>/dev/null; then echo "服务 $service_name 已安装。" 已…

项目部署问题bug记录(长期更新)

一、编译相关 1.submodules/simple-knn/simple_knn.cu(90): error: identifier "FLT_MAX" is undefined me.minn { FLT_MAX, FLT_MAX, FLT_MAX }; 部署photoreg工程&#xff0c;在编译simple_knn的时候&#xff0c;报错&#xff1a; (photoreg) leelee…

ant-design-vue中table组件多列排序

antD中table组件多列排序 使用前注意实现效果图实现的功能点及相关代码1. 默认按某几个字段排序2. 点击排序按钮可同时对多个字段进行排序3. 点击重置按钮可恢复默认排序状态。 功能实现完整的关键代码 使用前注意 先要确认你使用的antD版本是否支持多列排序&#xff0c;我这里…

影视后期学习Ⅰ~

1.DV是光盘 磁带 2.序列就是我们要制作的一个视频。 打开界面显示&#xff1a; 一号面板放的是素材&#xff0c;二号面板叫源监视器面板&#xff08;它的名字需要记住&#xff09;在一号面板点击文件之后&#xff0c;进入二号面板&#xff0c;在二号面板预览没问题后&#xf…

大语言模型---Llama模型文件介绍;文件组成

文章目录 1. 概要2. 文件组成 1. 概要 在使用 LLaMA&#xff08;Large Language Model Meta AI&#xff09;权重时&#xff0c;通常会涉及到与模型权重存储和加载相关的文件。这些文件通常是以二进制格式存储的&#xff0c;具有特定的结构来支持高效的模型操作。以下以Llama-7…

elasticsearch介绍和部署

1 elasticsearch介绍 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。可以很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性。Elasticsearch 的实现原理主要分为以下几个步骤&#xff0c;首先用户将数据提交到Elasticsea…

ZYNQ-7020嵌入式系统学习笔记(1)——使用ARM核配置UART发送Helloworld

本工程实现调用ZYNQ-7000的内部ARM处理器&#xff0c;通过UART给电脑发送字符串。 硬件&#xff1a;正点原子领航者-7020 开发平台&#xff1a;Vivado 2018、 SDK 1 Vivado部分操作 1.1 新建工程 设置工程名&#xff0c;选择芯片型号。 1.2 添加和配置PS IP 点击IP INTEGR…

Jenkins更换主题颜色+登录页面LOGO图片

默认主题和logo图片展示 默认主题黑色和白色。 默认LOGO图片 安装插件 Login ThemeMaterial Theme 系统管理–>插件管理–>Available plugins 搜不到Login Theme是因为我提前装好了 没有外网的可以参考这篇离线安装插件 验证插件并修改主题颜色 系统管理–>A…

《操作系统》实验内容 实验二 编程实现进程(线程)同步和互斥(Python 与 PyQt5 实现)

实验内容 实验二 编程实现进程&#xff08;线程&#xff09;同步和互斥 1&#xff0e;实验的目的 &#xff08;1&#xff09;通过编写程序实现进程同步和互斥&#xff0c;使学生掌握有关进程&#xff08;线程&#xff09;同步与互斥的原理&#xff0c;以及解决进程&#xf…

【倍数问题——同余系】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e5 10, M 1e3 10; int maxx[M][4]; void consider(int r, int x) {if(x > maxx[r][1]){maxx[r][3] maxx[r][2];maxx[r][2] maxx[r][1];maxx[r][1] x;}else if(x > maxx[r][2]){maxx[…

结合第三方模块requests,文件IO、正则表达式,通过函数封装爬虫应用采集数据

#引用BeautifulSoup更方便提取html信息&#xff1b;requests模块&#xff0c;发生http请求&#xff1b;os模块&#xff0c;文件写入import requests from bs4 import BeautifulSoup import os#当使用requests库发送请求时&#xff0c;如果不设置User - Agent&#xff0c;默认的…

Linux虚拟机网络配置

Linux固定IP 跳转到 cd /etc/sysconfig/network-scripts/ 打开文件并编辑 vim ifcfg-ens33 增加或修改选中内容 重启网卡 systemctl restart network ifconfig -a 查看ip已固定 虚拟机网络编辑器调整 子网IP进行修改&#xff0c;例如本机IP修改为10.212.197.34 此处就修改…

CSS实现实现当文本内容过长时,中间显示省略号...,两端正常展示

HTML 结构解析 文档结构: <ul class"con">: 一个无序列表&#xff0c;包含多个列表项。 每个 <li class"wrap"> 表示一个列表项&#xff0c;内部有两个 <span> 元素&#xff1a; <span class"txt">: 显示文本内容。<…

排序算法:直接插入排序,希尔排序,选择排序,快速排序,堆排序,归并排序

1.直接插入排序 基本思想&#xff1a;把待排序的数按照大小逐个插入到前面已经排序好的有序序列中&#xff0c;直到所有的都插入完为止&#xff0c;得到一个新的有序序列。 如图所示&#xff0c;当插入第i个&#xff08;i>1&#xff09;元素的时候&#xff0c;前面的arr[0]…

Qt:信号槽

一. 信号槽概念 信号槽 是 Qt 框架中一种用于对象间通信的机制 。它通过让一个对象发出信号&#xff0c;另一个对象连接到这个信号的槽上来实现通信。信号槽机制是 Qt 的核心特性之一&#xff0c;提供了一种灵活且类型安全的方式来处理事件和数据传递。 1. 信号的本质 QT中&a…

aws凭证(一)凭证存储

AWS 凭证用于验证身份&#xff0c;并授权对 DynamoDB 等等 AWS 服务的访问。配置了aws凭证后&#xff0c;才可以通过编程方式或从AWS CLI连接访问AWS资源。凭证存储在哪里呢&#xff1f;有以下几个方法&#xff1a; 一、使用文件存储 1、介绍 文件存储适用于长期和多账户配置…