之前我们了解到构造函数是在对象实例化之时对对象完成初始化工作的一个函数。在我们不写时,编译器会自动生成构造函数。构造函数有一些特点,比如,他对内置类型不做处理,对自定义类型的成员会去调用其自身的构造。

        我们上篇文章还提到了,默认成员函数不仅仅指我们不写编译器自动生成的函数,当我们不传参数,编译器会自动调用的函数,均为默认成员函数。

一、再探构造函数

  • 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个逗号分隔的数据成员列表,每个“成员变量”后边跟一个放在括号中的初始值或表达式。
  • 语法上理解,初始化列表可以认为是每个成员变量定义初始化的地方。

      ※※每个成员变量在初始化列表中只能出现一次,这个很重要,编译会报错。

  • 当然,函数体内的初始化和初始化列表初始化是可以同时存在的,这里会有同学疑惑,既然函数体内也可以进行初始化,为什么要延伸出初始化列表这个概念呢?要记住,存在即合理
  • 引用成员变量const成员变量没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则编译会报错。
  • 首先,我们来看,引用成员变量const成员变量有啥相似,那就是这两者必须在定义时就初始化

如下图,当在函数体内对这两种变量进行初始化时,编译会报错。

然而,当我们试图修改这种初始化方式,试图将_ref变为year的别名,_i变为day的别名,虽然从下图可以看出,语法上是没什么问题的,可以运行成功,但是,要考虑到year、month、day均是形参,当出了函数作用域,他们均将被销毁,这时的_ref、_i就变成了野引用,相当于野指针,是极其危险的。

  • 同时,第三个特殊的变量是没有默认构造的类类型变量需要在定义时显式地调用构造函数传参给值所以也需要在定义之时进行初始化。这里要注意一点,如果图中的Time类的构造函数有缺省值,就不需要在初始化列表进行初始化了。

  • 严格来说,这些声明过的成员变量不管是否在初始化列表中出现都会走一遍初始化列表。
  • 还有一点,我们可以看到_day编译器只赋了一个随机值,这也是我们之前说过的C++对内置类型的初始化是不做处理的,事实上,_day也走了初始化列表。

引用成员变量的初始化可以参考下图:

在声明处也可以这样写,相当于给成员变量缺省值


※※※按声明顺序(与初始化列表出现顺序无关),所以建议同学们将声明顺序与初始化列表顺序保持一致。

这里要问大家一个问题:这里最终的初始化结果是什么?

答案是_i = 1。

  • 首先,我们要明确,_i如果在声明处给了缺省值3,假设我们没有在初始化列表中显式对_i进行初始化,_i还是会走初始化列表,但程序是没有错误的。
  • 其次,由于_i在初始化列表进行了初始化,且值为1,我们可以从上面给大家提供的逻辑分析,由于成员变量已经显式初始化,就不会再考虑未初始化的情形了。

       

  有同学又要问,我们可不可以只用初始化列表初始化,不在函数体内部了,当然是不行的了!还是那句话:存在即合理!像这一类,需要函数逻辑来进行初始化的成员,当然还是需要在函数体内进行初始化。

建议之后初始化将缺省值、初始化列表、函数体结合起来共同实现,因为我们的成员变量(包括未显式初始化的成员)都要走一遍初始化列表,有些初始化逻辑又必须使用函数,我们何必要浪费资源,不如都应用起来!~

 二、类型转换

        C语言阶段我们也曾提到过类型转换,内置类型隐式转换,前提是他们之间是有关联的,比如整型之间的转换,int可以转换为short;比如整形和浮点数之间也可以进行相互转化,int转换为double;再比如整型和指针的转换;指针和指针之间也可以相互转换。

  • C++支持内置类型隐式转换为类类型对象,需要有相关内置类型为参数的构造函数

因此,也就引申出了:

有同学会问,这种场景现实吗?由于r1是别名,这里的1隐式转换为A,为临时对象,其实是可以的。但临时对象具有常性,这是我们之前就了解过的,所以这里应该在A前加const。

 在之前的学习中,我们了解到,类型转换会构造临时对象,临时对象又具有常性。

  • 构造函数前面加explicit就不再支持隐式类型转换。

同时,要注意一个问题,当我们对多内置类型的对象传值时,使用(1,1)会被误认为是逗号表达式,从而只传了一个值,在此,我们可以使用{1,1}。

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

三、static成员

      提出一个需求,实现一个类,计算程序中创建出了多少个类对象?最能想到的方式就是定义一个全局变量_scount,每当创建一个对象调用一次拷贝构造,_scount就加1。但我们上面提到过,当编译器同时遇到连续构造和拷贝构造,就会采用优化,变为直接构造,除非关闭优化。所以到底程序创建了多少对象,是算不明白的。

还有一个弊端,_scount作为全局变量,在任何类、函数里都可以修改,是否可以定义一个专属于一个类的全局变量呢?

答案是可以的。这个变量就叫做静态成员变量,它不属于某个对象,而是属于整个类,属于这个类的所有对象,相当于“类中的全局变量”。属于静态区,不存在对象中,并且受访问限定符的限制,不会轻易改变。

再一个问题,static静态成员变量是不可以给缺省值的,由于它不参与对象的创建,不会走初始化列表,更不会使用缺省值。

※※※静态成员变量要在类内声明,类外定义。

类中的静态成员变量怎样访问呢?

假设设置他为公有,那么只要指定类域or用对象访问就可以使用(因为这两种情况都可以让编译器识别到_scount这个静态变量是属于A类的),但是会遇到与上面提到过的全局变量一样的问题,有随时被修改的风险;

假设设置为私有,我们可以设置一个公有的成员函数,有点类似于Java中的get/set()。

 除了静态成员变量,还有静态成员函数。所谓静态,就是在函数返回类型前加上static。

※很重要的一点,静态成员函数的特点是没有this指针!

也就是如果对象中有非静态的成员变量,在静态成员函数中是不能访问的!

静态成员函数应用场景:

解锁访问静态成员函数的两种姿势:

	cout << A::Get() << endl;cout << aa1.Get() << endl;

四、友元 

  • 我们在类外面是不能访问私有或保护成员的,友元则提供了一种突破类访问限定符封装的方式。
  • 友元分为:友元函数友元类
  • 在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。
  • 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
  • 友元函数可以在类定义的任何地方声明,不受访问限定符限制。
  • 一个函数可以是多个类的友元函数。

  • 友元类的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
  • 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但B类不是A类的友元。

  • 友元类不能传递,如果A是B的友元,B是C的友元,但A不是C的友元。
  • 友元有时提供了便利,但是友元会增加耦合度,破坏了C++的封装特性,所以友元不宜多用。 

五、内部类

  • 如果一个类定义在另一个类内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类的类域限制访问限定符限制,所以外部类定义的对象中不包含内部类。
  • 如果内部类在外部类中定义为私有,那么这个内部类就是外部类的专属类。
  • 内部类默认是外部类的友元。
  • 这里说明,内部类在外部类的类域里面,突破类域,可以直接访问静态成员变量
  • 由于B(内部类)默认是A(外部类)的友元,所以非静态成员变量需要通过调用对象进行访问
  • 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。

六、匿名对象

  • 匿名对象,从他的名字可以看出,他是没有名字的对象,也就是在定义时不起名字;我们在之前定义有名对象时,曾提到调用无参构造时不要写括号,会与函数声明冲突,分不清楚,与有名对象不同,匿名对象在调用无参构造时要加上括号,如果不加,就很容易迷惑人,不知道在写什么。
  • 它是由我们主动写的,并非编译器生成的。
  • 匿名对象还有一个显著的特点:它的生命周期只在当前一行。即用完就被销毁掉了。

七、对象拷贝时的编译器优化

  • 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下,尽可能减少一些传参和传返回值的过程中可以省略的拷贝。
  • 对于如何优化,C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流、相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新的编译器还会进行跨行跨表达式的合并优化。

(传值传参)以下两种情况,将两个构造过程合二为一,提高了程序效率:


(传值返回)

总结一下:

  • 如果用对象向函数进行传值传参,尽可能用匿名对象or隐式类型转换的方式来替代有名对象(无优化)
  • 如果传值返回,接收返回值更推荐使用拷贝构造的方式(也就是上上图的第一种方式)

        未完待续…点个赞呗~~ (2025/7/17/20:03:19)

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

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

相关文章

Flutter基础(前端教程①②-序列帧动画)

&#x1f9e0; 核心思路总结​​彻底绕过 Image组件重建带来的性能瓶颈​​&#xff1a;不再让 setState重建包含 Image的 Widget 树&#xff08;这是开销大、可能导致闪烁的根源&#xff09;&#xff0c;改为使用底层画布 (Canvas) 直接绘制预先处理好的图像帧数据。好的&…

Qt添加dmp文件生成及pdb文件

1.Pdb文件生成 下图先通过构建生成Pdb文件&#xff0c;然后运行程序&#xff0c;通过提前准备的崩溃按钮使得程序崩溃&#xff0c;生成“dump文件”的演示。 # #添加dmp文件生成及pdb文件生成DEFINES QT_MESSAGELOGCONTEXT DEFINES QT_DEPRECATED_WARNINGS# # 添加DUMP文件…

opencv、torch、torchvision、tensorflow的区别

一、框架定位与核心差异PyTorch动态计算图&#xff1a;实时构建计算图支持Python原生控制流&#xff08;如循环/条件&#xff09;&#xff0c;调试便捷。学术主导&#xff1a;2025年工业部署份额24%&#xff0c;适合快速原型开发&#xff08;如无人机自动驾驶、情绪识别&#x…

离散与组合数学 杂记

生成函数 概念 又称母函数把一个无穷数列 {an}\{a_n\}{an​}&#xff08;默认从 000 项起&#xff09;表示成 G(x)∑i≥0aixiG(x)\displaystyle\sum_{i\ge0} a_ix^iG(x)i≥0∑​ai​xi 的函数形式。例如&#xff1a; ai2ia_i2^iai​2i&#xff1a;G(x)∑i≥02ixiG(x)\display…

学习OpenCV---显示图片

学习OpenCV—显示图片 最近在学习OpenCV入门&#xff0c;于是记录一下自己的学习过程。 一、配置环境 第一步 从官方网站中下载OpenCV开源库。官方下载网站 打开官网后&#xff0c;能看到有很多的版本。我个人下载的是4.11.0版本。点击图中的下载 下载完成后&#xff0c;解…

第一次接触自动化监测,需要付费厂家安装服务吗?比人工测量主要区别是啥?

人工检测是依靠目测检查或借助于便携式仪器测量得到的信息&#xff0c;但是随着整个行业的发展&#xff0c;传统的人工检测方法已经不能满足检测需求&#xff0c;从人工检测到自动化监测已是必然趋势。 a. 从检测方式看 人工检测需要耗费大量的精力&#xff0c;从摆放检测工具到…

VMware Workstation Pro 17下载安装

注册账号 进入下载地址&#xff1a;Free Downloads - Support Portal - Broadcom support portal - https://support.broadcom.com/ 会让注册账号&#xff0c;注册一个就行 在右上角 下载 地址&#xff1a;Free Downloads - Support Portal - Broadcom support portal - ht…

SpringBoot 3.x集成阿里云OSS:文件上传 断点续传 权限控制

SpringBoot 3.x集成阿里云OSS&#xff1a;文件上传&#xff0f;断点续传&#xff0f;权限控制Spring Boot 3.x 集成阿里云 OSS 终极指南一、环境准备与依赖配置1. 添加阿里云 OSS SDK 依赖2. 配置 OSS 连接参数二、基础文件上传服务1. OSS 客户端配置2. 文件上传服务三、断点续…

牛客周赛 Round 100

A小红的双排列没什么好说的 直接 1 1 2 2 3 3 4 4……#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<iostream> #include<bits/stdc.h> #define ll long long using namespace std; int n; int main(){ios::sync_with_stdio(false); …

【Dv3Admin】菜单管理集成阿里巴巴自定义矢量图标库

图标选择是后台管理系统中高频功能。相比用 Element UI、Ant Design 等自带的 icon 集&#xff0c;阿里巴巴 iconfont.cn 支持上传和管理自定义图标&#xff0c;并生成矢量字体&#xff0c;便于统一维护和扩展。 本文目标是支持自定义 iconfont 图标的展示和选择&#xff0c;并…

NO.7数据结构树|线索二叉树|树森林二叉树转化|树森林遍历|并查集|二叉排序树|平衡二叉树|哈夫曼树|哈夫曼编码

线索二叉树 线索二叉树的基本概念 为了解决无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题&#xff0c; 出现了线索二叉树。 一个二叉树通过如下的方法“穿起来” &#xff1a; 所有原本为空的右(孩子)指针改为指向该节点在某种遍历序列中的后继&#xff0c; 所有原…

R语言基础| 基本图形绘制(条形图、堆积图、分组图、填充条形图、均值条形图)

目录 一、前言 二、条形图 1. 简单的条形图 2.堆积、分组和填充条形图(柱状图) &#xff08;1&#xff09;堆积图&#xff0c;对Improved进行堆积&#xff0c;注意position“stack” &#xff08;2&#xff09;分组图&#xff0c;对Improved进行分组&#xff0c;注意posit…

SegNet:一种用于图像分割的深度卷积编码器解码器架构

教程/讲解视频点击文末名片1、什么是语义分割&#xff0c;什么是FCN 我们提出了一种新颖且实用的深度全卷积神经网络架构&#xff0c;用于语义像素级分割&#xff0c;命名为SegNet。 语义分割是指为图像中的每个像素分配一个类别标签&#xff08;如道路、天空、汽车&#xff09…

PyTorch 数据加载全攻略:从自定义数据集到模型训练

目录 一、为什么需要数据加载器&#xff1f; 二、自定义 Dataset 类 1. 核心方法解析 2. 代码实现 三、快速上手&#xff1a;TensorDataset 1. 代码示例 2. 适用场景 四、DataLoader&#xff1a;批量加载数据的利器 1. 核心参数说明 2. 代码示例 五、实战&#xff1…

Python--plist文件的读取

Python练习&#xff1a;读取Apple Plist文件 Plist文件简介 ​​定义​​&#xff1a;Apple公司创建的基于XML结构的文件格式​​特点​​&#xff1a;采用XML语法组织数据&#xff0c;可存储键值对、数组等结构化信息文件扩展名​​&#xff1a;.plist应用场景: ​​iOS系统:​…

JAVA几个注解记录

在Java中&#xff0c;Data、AllArgsConstructor和NoArgsConstructor是Lombok库提供的注解&#xff0c;用于自动生成Java类中的样板代码&#xff08;如getter、setter、构造函数等&#xff09;&#xff0c;从而减少冗余代码&#xff0c;提高开发效率。以下是它们的详细功能和使用…

js对象简介、内置对象

对象、内置对象 jarringslee 对象 对象&#xff08;object&#xff09;是js的一种引用数据类型&#xff0c;是一种无序的数据集合“ul”&#xff08;类比于数组&#xff0c;有序的数据集合“ol”&#xff09;。 基本上等于结构体。 对象的声明 //基本方法 let 对象名 {声…

【工程篇】07:如何打包conda环境并拷贝到另一台服务器上

这是一份以名为 qwen2.5-vl 的 Conda 环境为例的详细操作手册&#xff0c;指导您如何将其打包并迁移至另一台服务器。操作手册&#xff1a;迁移 Conda 环境 qwen2.5-vl 至新服务器 本文档将提供两种有效的方法来迁移您的 qwen2.5-vl 环境。请根据您的具体需求和服务器条件选择最…

rustdesk远控电脑替代todesk,平替向日葵等软件

rustdesk网页端远控电脑docker run --restart always \ --privileged \ -p 9000:9000 \ -p 21114:21114 \ -p 21115:21115 \ -p 21116:21116 \ -p 21116:21116/udp \ -p 21117:21117 \ -p 21118:21118 \ -p 21119:21119 \ -e KEYj8muHpzr2HK00zm9D94b1UFkaJ1bEiWsyA1qxb1nOA \ …

板凳-------Mysql cookbook学习 (十二--------1)

第9章 存储例程&#xff0c;触发器和计划事件 326 9.0 概述 326 9.1 创建复合语句对象 329 mysql> -- 恢复默认分隔符 mysql> DELIMITER ; mysql>mysql> DROP FUNCTION IF EXISTS avg_mail_size; Query OK, 0 rows affected (0.02 sec)mysql> DELIMITER $$ mysq…