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

1.初始化列表

初始化列表是集成在构造函数里面的,对象在创建的时候一定会调用构造函数(就算不显式定义,也会自动生成并调用)。初始化列表就是这些对象的成员变量在创建的时候初始化的地方。

下面是使用的例子,可以先看看:

注意:这个格式只能是:冒号开始,逗号分隔,成员变量后只能用括号(包括对象、赋值变量),不能使用赋值=,内置类型括号内是什么赋值什么,自定义类型就用括号里的值调用它的构造函数

我们需要将这种写法和我们之前写的构造函数做对比:

这个特性在某些场景非常关键,在以下两种场景中必须使用初始化列表:

(1)有的变量只有在初始化的时候赋值,比如引用int&(不能出现空引用),const修饰的变量(常属性)

(2)当我们不显式实现构造函数时,编译器会自动生成默认构造函数,其中的规则有一条为——对于自定义类型会去调用它的默认构造。这里有个问题就是,如果这个自定义类型显式实现了一个带参数的构造函数,那么它无法生成默认构造,编译器是无法调用它的,这个时候就会报错。

下面是实例:

我们发现如果没有初始化列表,这两种情况是无解的。对于(1),不管我们怎么写构造函数,引用和const变量在创建时都无法被赋值。对于(2)更是如此,根本不支持在大括号里去调用构造函数,只有初始化的时候才可以。

2.缺省值和初始化列表的关系

之前我们就提到调用构造函数前会先走一遍缺省值,后续的调用本质上是一种覆盖。

更准确地说,缺省值是给初始化列表使用的。假设我们在成员变量声明处写了int _a = 1,如果我们没有自己写初始化列表,而是在函数体内写了_a = 2,那么当_a创建时会自动创建一个初始化列表,其中_a初始化时在初始化列表中赋的值就是缺省值,也就是_a(1),后续再进入函数体,将_a赋值为2。

值得注意的是初始化列表中对成员变量进行初始化的顺序是按照在类中声明的顺序进行的,而不是按照初始化列表中代码的顺序进行的,同样地,这些成员变量在空间中开辟的顺序就是按照声明顺序进行的。

如果我们成员变量中有自定义类型的对象,且这个自定义类型中有含参的构造函数,那么我们一定要显式实现这个初始化列表,否则一定会报错。

如果我们显式实现了初始化列表,如在初始化列表中写了_a(1),在成员变量声明处写了缺省值int _a = 2,这个时候在编译器创建变量并初始化时就会直接忽略我们声明处的缺省值。

初始化列表或者缺省值处我们可以自由地写表达式或者函数,都能实现我们想要的效果。

3.内置类型隐式转换成自定义类型

这是一个相对来说比较难理解的地方,我们所知的int、char、double、int*等可以转换成类的类型,如A a = 3。这里需要注意的是数组不是内置类型,它属于自定义类型(int []),因此如A a = "Hello"这种操作是不行的。

转换的实质:

默认生成拷贝构造是const A& tmp,有const修饰,也不需要担心默认的拷贝构造会被const拦截住

注意这里是我们的逻辑,而编译器会进行优化,实际并不是这样,后面会提及。

但是,不是所有的情况都可以实现隐式类型转换,是要看自定义类型的构造函数有几个参数来决定的。下面我分情况来讲解一下:

(1)无参的构造函数:不支持任何隐式类型转换

(2)含一个参数的构造函数:支持所有内置类型的转换,转换的规则就是创建一个临时的对象(这个对象具有常属性,里面成员变量的值不能修改),这个内置类型的值就作为它构造函数的参数传过去。

传的参数没有要求,遵循C语言内置类型之间的隐式类型转换规则。

(3)多个参数:支持所有内置类型的转换,但根据规则我们需要多个内置类型才能隐式转换成自定义类型,用大括号括起来。注意内置类型的数量要和构造函数的参数个数相同,除非有缺省值,这和我们之前遇到的构造函数的初始化类似。

不加=也是可以的,要学会识别。

当然,隐式类型转换也存在它的风险,如果我们不想发生隐式类型转换,可以在构造函数前面加上explicit修饰

注意隐式类型转化也可以嵌套,在有的情况下还是很好用的


#include <iostream>
using namespace std;class A
{
public:A(int a = 0, int b = 0):_a(a),_b(b){}int _a;int _b;
};class B
{
public:B(int c, A d):_c(c),_d(d._a, d._b){}private:int _c;A _d;};int main()
{B b1 = { 1,{2,3} };return 0;
}

4.友元

就在上面的那段代码,我们会注意到A的_a和_b是共有的,如果是私有的,那么如何处理呢?

(1)友元类

首先像构造和析构函数这种特殊的函数没有返回值,不是标准的函数形式,所以无法使用友元函数。因此我们要使用友元类来处理这种情况。声明友元类后,A的友元B可以访问A的所有私有变量以及所有的函数。


#include <iostream>
using namespace std;class A
{friend class B;public:A(int a = 0, int b = 0):_a(a),_b(b){}
private:int _a;int _b;
};class B
{
public:B(int c, A d):_c(c),_d(d._a, d._b){}private:int _c;A _d;};int main()
{B b1 = { 1,{2,3} };return 0;
}

(2)友元函数

如果B类或全局的函数想要访问A类的私有成员,我们可以采用友元函数的方式,用friend+函数声明即可。一个函数可以是多个类的友元函数,访问的时候和普通函数相同。

要注意友元函数的几个特性:

a.友元函数不能用const修饰(规定)

b.在A声明的友元函数并不是A的成员函数,也没有this的概念

c.友元函数可以在类定义的任何地方声明(不受访问限制符private的作用)

(3)内部类

内部类和我们理解的成员变量不太一样,它是独立的,在计算sizeof的时候不会计算内部类的大小。其实内部类仅仅是受到类域的限制,表明它是这个外部类的专属类。

内部类默认就是外部类的友元,内部类可访问外部类的私有成员;但是外部类并不默认是内部类的友元函数,不能直接访问。


 

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

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

相关文章

【CVPR2023】《A2J-Transformer:用于从单个RGB图像估计3D交互手部姿态的锚点到关节变换网络

这篇论文的标题是《A2J-Transformer: Anchor-to-Joint Transformer Network for 3D Interacting Hand Pose Estimation from a Single RGB Image》&#xff0c;作者是Changlong Jiang, Yang Xiao, Cunlin Wu, Mingyang Zhang, Jinghong Zheng, Zhiguo Cao, 和 Joey Tianyi Zhou…

7. DAX 时间函数-- DATE 日期--TOTALMTD、TOTALQTD、TOTALYTD

函数名目的语法返回值TOTALMTD计算当前上下文中该月份至今的表达式的值 。TOTALMTD ( <表达式>, <日期列>, [<筛选器>] )标量 表示表达式的标量值&#xff0c;在“日期”中给定日期&#xff0c;计算当前月份至今的日期 。TOTALQTD计算当前上下文中该季度至今…

k8s 部署 kube-prometheus监控

一、Prometheus监控部署 1、下载部署文件 # 使用此链接下载后解压即可 wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/heads/release-0.13.zip2、根据k8s集群版本获取不同的kube-prometheus版本部署 https://github.com/prometheus-operator/k…

Unity实现动态数字变化

最近的项目需要动态显示数字&#xff0c;所以使用Text组件&#xff0c;将数字进行变化操作过程记录下来。 一、UI准备 1、新建一个Text组件 2、新建C#脚本 3、将Text挂载到脚本上 二、函数说明 1、NumberChange 方法 NumberChange 方法接收四个参数&#xff1a;初始数字 in…

页面动态滑动事件如何实现?锚点如何实现?

在提交材料、页面互动时候&#xff0c;锚点是一个常用的东西&#xff0c;实现: 应用场景测试场景测试源码 应用场景 测试场景 当点击submit时候&#xff0c;即可滑动到对应的id标签 测试源码 <!DOCTYPE html> <html lang"en"> <head><meta …

云架构(五)BBF模式

BFF模式&#xff08;Backends for Frontends pattern&#xff09;- https://learn.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends。 创建单独的后台服务用以提供给特定的前端或者接口。当你希望避免为多个接口定制单独的后台时&#xff0c;此模…

Jenkins和gitlab实现CICD

1 背景 在开发TracerBackend服务的时候&#xff0c;每次更改代码之后需要推送到gitlab&#xff0c;然后ssh登录到Ubuntu的服务器上部署新的代码。服务成功启动之后&#xff0c;在本地执行测试用例&#xff0c;觉得这一套操作流程还是挺复杂的。想起公司的代码发布流程&#xf…

黑马程序员Linux简单入门学习笔记

Linux介绍 内核提供系统最核心的功能&#xff0c;如: 调度CPU、调度内存、调度文件系统、调度网络通讯、调度等系统级应用程序&#xff0c;可以理解为出厂自带程序&#xff0c;可供用户快速上手操作系统&#xff0c;如:文件管理器、任务管理器、图片查看、音乐播放等 目录结构 …

【Java网络编程】网络编程中的基本概念及实现UDP、TCP客户端服务器程序

目录 一、什么是网络编程&#xff1f; 二、网络编程中的基本概念 1. 客户端和服务器 2. 请求和响应 三、Socket套接字 UDP数据报套接字编程 1. DatagramSocket 2. DatagramPacket 3. UDP回显客户端服务器程序 4. UDP字典客户端服务器程序 TCP流套接字编程 1. Serve…

嵌入式linux中利用QT控制蜂鸣器方法

大家好,今天给大家分享一下,如何控制开发板上的蜂鸣器。 第一:开发板原理图 从原理图中可以得出,当引脚输出低电平的时候,对应的蜂鸣器发出响声。 第二:QT代码详细实现 设置一个按钮,点击即可控制BEEP状态发生反转。 #ifndef MAINWINDOW_H #define MAINWINDOW_H#in…

从0到1实现RPC | 接入Apollo配置中心

一、代码实现 添加依赖 添加apollo客户端的依赖和spring配置相关依赖 添加监听器 通过实现ApplicationContextAware接口&#xff0c;获取Spring上下文。 使用ApolloConfigChangeListener注解监听命名空间rpc-demo-provider.yaml和默认的application.properties。 监听逻辑…

STL的map:ALV树和红黑树

ALV树 平衡因子的几种情况 单旋 双旋 红黑树 三种情况 第二种情况变种&#xff1a;不同的是折线要双旋 总结&#xff1a;