C++——类和对象(初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类)
初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类
本章思维导图:

注:本章思维导图对应的xmind文件和.png文件都已同步导入至资源
文章目录
- 初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类
- 1. 初始化列表
- 1.1 再谈构造函数
- 1.2 初始化列表
- 1.2.1 初始化列表的语法
- 1.2.2 初始化列表的意义
- 1.3 注意事项
- 2. 匿名对象
- 3. static成员
- 3.1 static成员变量
- 3.2 static成员函数
- 4. 类的隐式类型转换和explicit关键字
- 4.1 类的隐式类型转换
- 4.1 explicit关键字
- 5. 内部类
1. 初始化列表

1.1 再谈构造函数
众所周知,每个变量只能被初始化一次,我们之前一直认为成员变量的初始化是在构造函数的函数体中,但是,成员变量是可以在构造函数的函数体出现多次的:
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;//出现多次,且可以编译通过_year = 100;_month = 200;}
private:int _year;int _month;int _day;
};
因此,我们只能认为在构造函数函数体内执行的是赋值操作,而不是初始化
这就说明,构造函数的函数体并不是类的成员变量真正初始化的地方,那么成员变量到底是在哪里初始化的呢?
1.2 初始化列表
初始化列表是成员变量真正初始化的地方
1.2.1 初始化列表的语法
初始化列表以分号
:开始,以逗号,分割,每个成员变量后面带上放在括号()里的初始值或者表达式
例如,对于上面的构造函数:
Date(int year = 1, int month = 1, int day = 1)//初始化列表: _year(year), _month(month), _day(day)
{}
1.2.2 初始化列表的意义
初始化列表解决了三类不能在构造函数的函数体内初始化的问题:
&修饰的引用成员变量——引用成员在定义时就必须初始化const修饰的const成员变量——const变量在定义时就必须初始化- 没有默认构造的自定义类型——在函数体内不能初始化自定义类型
也就是说,上面所说三类成员变量必须在初始列表里面进行初始化。
例如;
class Stack
{
public://这不是默认构造,因为要传参数Stack(int capacity){}private:int* _a;int _capacity;int _top;
};class Date
{
public:Date(int year = 1, int month = 1, int day = 1): num1(2), num2(_year), st(3){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;const int num1;int& num2;Stack st;};
1.3 注意事项
-
因为初始化列表是成员变量初始化的地方,而每个变量又只能初始化一次,因此成员变量只能在初始化列表出现一次
-
因为初始化列表是真正初始化成员变量的地方,因此无论有没有显示的写出初始化列表,成员变量都会经过初始化列表的初始化。
-
如果没有显示的写出初始化列表,那么:
- 对于内置类型,那就赋予其初始值
- 对于自定义类型,就调用它的默认构造
-
能使用初始化列表就使用初始化列表。但也不是说初始化列表就能完全替代函数体。因为有时候函数体需要进行检查等操作。
-
初始化列表的初始化顺序是成员变量声明的顺序,而不是在初始化列表里出现的顺序。
class A { public:A(): a1(1), a2(a1){}void Print(){cout << a1 << endl << a2 << endl;} private:int a2;int a1; };int main() {A a;a.Print();return 0; }/*output:1-858993460 */ //a2声明在a1之前,因此,在初始化时,先执行a2(a1),此时a1为随机值 //因此建议成员变量的初始化顺序和声明顺序一致
2. 匿名对象

class Date
{
public:Date(int year = 1, int month = 1, int day = 1): _year(year), _month(month), _day(day){myCount++;}void Print(){cout << "Date" << endl;}
private:int _year;int _month;int _day;
};
如果我们想不实例化对象,但想调用Date类里的Print()函数来知道这是个什么类,该如何做到呢?
这里就可以用到我们的匿名对象来解决:
int main()
{Date().Print(); //Date()创建出一个匿名对象,再用这个匿名对象来调用成员函数Print()return 0;
}
创建匿名对象的方式:
className()
匿名对象的特点:
- 匿名对象是一种临时对象,它没有分配给任何命名变量,而是在需要时被创建并使用
- 其生命周期仅存在于当前行,执行完后立即销毁
- 匿名对象一般是常量对象,不可被修改
3. static成员

如果我们想记录一个类究竟被构造了多少次
我们不难写出这样的代码:
//定义一个全局变量来记录类A构造的次数 int myCount = 0;class A { public://构造函数A(){myCount++;}//拷贝构造A(A& a){myCount++;} };int main() {A a[10];cout << myCount << endl;return 0; }但是这就出现了一个问题:我们可以在全局随意修改变量
myCount的值:int main() {A a[10];myCount++;cout << myCount << endl;return 0; }这样也就不能保证类被构造次数的正确性了。
3.1 static成员变量
为了解决这个问题,我们可以在类里面声明一个static成员,并用这个成员来记录类被构造的次数:
class A
{
public://构造函数A(){myCount++;}//拷贝构造A(A& a){myCount++;}private:static int myCount;
};int A::myCount = 0;
这个由static修饰的静态成员变量有如下特点:
- 这实际上也是一个全局变量,只是受类域和访问限定符所限制
- 静态成员变量只能在类里面声明在类外面定义。
- 静态成员变量在声明时不能和非静态成员变量一样给缺省值,因为这个缺省值是给初始化列表里用的,而静态成员变量不用初始化列表初始化。
- 由
static修饰的静态成员变量是这个类所属的,而不是由这个类实例化的某个对象所独有的
3.2 static成员函数
知道如何利用
static成员变量之后,针对最开始的问题,我们不难写出下面的代码:class A { public://构造函数A(){myCount++;}//拷贝构造A(A& a){myCount++;}//因为myCount被private修饰,在类外面无法访问//因此要用成员函数访问myCountint GetCount(){return myCount;}private:static int myCount; };int A::myCount = 0;int main() {A a[10];//为了调用GetCount成员函数,必须要实例化一个对象,而这个对象是没有意义的,因此最终结果要减一cout << A().GetCount() - 1 << endl;return 0; }但是又有一个问题出现了:
我们只是想知道
A类到底被调用了多少次,但是要知道这个结果又必须新实例化一个对象,有没有什么方法不实例化对象就可以直接得到myCount的值呢?
为了解决上述问题,就需要用到static成员函数
class A
{
public://构造函数A(){myCount++;}//拷贝构造A(A& a){myCount++;}//static静态成员函数static int GetCount(){return myCount;}private:static int myCount;
};int A::myCount = 0;int main()
{A a[10];cout << A::GetCount() << endl;return 0;
}
由static修饰的静态成员函数有如下特点:
和静态成员变量一样,静态成员函数实际上也是一个全局函数,只是受类域和访问限定符限制
静态成员函数在类里面声明,但既可以在类外面定义也可以在类里面定义
和非静态成员函数不同,静态成员函数没有
this指针,因此静态成员函数无法访问非静态成员变量和非静态成员函数,但也因如此,它可以直接通过类名和域作用限定符::调用。

4. 类的隐式类型转换和explicit关键字
4.1 类的隐式类型转换
以前我们一般是这么实例化一个对象的:
class Date
{
public:private:
};int main()
{Date d1; //利用构造函数实例化对象Date d2(d1); //利用拷贝构造实例化对象return 0;
}
现在又有一个新的实例化对象的方法——类的隐式类型转换
class A
{
public:A(int a = 1): _a(a){}private:int _a;
};
int main()
{A A1 = 10;return 0;
}

可以看出,整形10确实被转换为了A类型。
根据当隐式类型转换发生时会产生临时变量的知识点,我们可以推导出A A1 = 10这行代码的具体实现逻辑:

应该清楚,要支持这种隐式类型转换,该类的构造函数应该支持只传一个内置类型就可以实现构造
例如对于下面几种情况,就不支持内置类型隐式转换为类类型:
//Error_1
class A
{
public:A(): _a(a){}private:int _a;
};
int main()
{A A1 = 10;return 0;
}
/*
报错:error C2065: “a”: 未声明的标识符error C2440: “初始化”: 无法从“int”转换为“A”message : 无构造函数可以接受源类型,或构造函数重载决策不明确
*///Error_2
class A
{
public:A(int a, int b): _a(a){}private:int _a;int _b;
};
int main()
{A A1 = 10;return 0;
}
/*
报错:error C2440: “初始化”: 无法从“int”转换为“A”message : 无构造函数可以接受源类型,或构造函数重载决策不明确
*/
类似的,对于有多个形参的构造函数,我们也可以传入多个内置类型进行构造:
class Date
{
public:Date(int year = 1, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};int main()
{Date d3 = {2023, 11, 7}; //传入三个内置类型进行构造return 0;
}

4.1 explicit关键字
有些时候,如果我们不想让上面所说的隐式类型转换发生,我们可以在构造函数的声明前加上explicit关键字:
explicit Date(int year = 1, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
加上explicit关键字后,如果继续进行隐式类型转换,就会报错:
error C3445: "Date" 的复制列表初始化不能使用显式构造函数
5. 内部类
C++支持在类的内部继续创建类,例如:
class A
{
public:class B{};private:int _a;int _b;
};
内部类有如下的特点:
-
内部类是一个独立的类,它不属于外部类,不能通过外部类的对象来访问内部类的成员
-
内部类天生就是外部类的友元类,可以直接访问外部类的成员变量和成员函数
class A { public:class B{void Print(A& a){a._a = 1;}public:int _b;};private:int _a; }; -
sizeof(外部类)的结果和内部类无关class A { public:class B{public:int _b;};private:int _a; };int main() {cout << sizeof(A) << endl;return 0; }//output:4
-
C++类和对象的知识到这里就学习完毕了,之后博主会发布C++类和对象的总结篇
-
下一篇,博主将介绍C++的内存管理,感兴趣的小伙伴可以来看看哦~

相关文章:
C++——类和对象(初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类)
初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类 本章思维导图: 注:本章思维导图对应的xmind文件和.png文件都已同步导入至资源 文章目录 初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类1.…...
高德地图撒点组件
一、引入amap地图库 - public/index.html <script type"text/javascript">window._AMapSecurityConfig {securityJsCode: 地图密钥 }</script><scripttype"text/javascript"src"https://webapi.amap.com/maps?v1.4.8&key111111…...
TCP/IP协议群
TCP/IP协议群 什么是TCP/IP协议群 从字面意义上讲,有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下,它只是利用 IP 进行通信时所必须用到的协议群的统称。具体来说,IP 或 ICMP、…...
esxi 6.7下安装黑裙
esxi上创建一个黑裙系统的虚拟机,用来存资料 一、工具 硬件: 工控机:装有esxi6.7系统(192.168.100.2),配置:3865U,16G内存,120Gmsata120sata硬盘,6个网口 主…...
C++初阶-类和对象(下)
类和对象(下) 一、再谈构造函数构造函数体赋值初始化列表explicit关键字 二、static成员概念特性 三、友元友元函数友元类 四、内部类五、匿名对象六、拷贝对象时的一些编译器优化七、再次理解类和对象 一、再谈构造函数 构造函数体赋值 在创建对象时&a…...
MD5校验 C语言实现 (附源码)
1.简介 MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。 MD5算法具有以下特点&am…...
成功解决/bin/sh: cc: command not found和/bin/sh: g++: command not found
成功解决/bin/sh: cc: command not found和/bin/sh: g: command not found 目录 解决问题 解决思路 解决方法 解决问题 make: cc: Command not found /bin/sh: cc: command not found expr: syntax error expr: syntax error make: cc: Command not found I llama.cpp buil…...
理解ELMo 模型
ELMo是一种用于处理自然语言的技术,它能够帮助计算机更好地理解词语在不同上下文中的含义。比如,在句子"他去银行取钱"("He went to the bank to withdraw money")和"他在河岸边钓鱼"(&…...
oracle 基础语法总结
常用简单查询汇总(必须掌握,记不住的收藏以备查看) 1、查询有奖金的员工: select* from emp where comm is not null; 2、查询没有奖金的员工信息: select * from emp where comm is null; 3、两个条件以上就得用and 如查询工资大于1500和有…...
Visual Studio 2017附加依赖项
在读韩国人尹圣雨的《TCP/IP网络编程》,在书中教我如何在Visual Studio 2008中设置附加依赖项,但是我使用的是Visual Studio 2017,所以我写下这篇文章学习如何在Visual Studio 2017附加依赖项。 在项目这里选择属性。 选择输入这一项,然后点…...
获取狮子座明年恋爱运势预测API接口
获取狮子座明年恋爱运势预测API接口的功能是通过API接口获取狮子座明年恋爱运势的预测结果,为用户提供恋爱运势指导。 首先,使用挖数据平台该API接口需要先申请API密钥。在获取API密钥后,可以使用该接口进行开发。 API接口地址为:…...
USB HID在系统下通信的一些总结
前言 这篇文章主要介绍在PC(上位机,Host)端,通过HID与硬件进行通信的一些总结,像很多同学肯定和我一样压根不想 去了解什么USB相关的资料,毕竟USB太复杂了,只想有个API给我们进行下数据就好了&…...
[java进阶]——方法引用改写Lambda表达式
🌈键盘敲烂,年薪30万🌈 目录 📕概念介绍: ⭐方法引用的前提条件: 1.引用静态方法 2.引用构造方法 ①类的构造: ②数组的构造: 3.引用本类或父类的成员方法 ①本类࿱…...
lvs dr+keepalived
基于keepalived(主从双主) LVS(DR模型) DNS实现http高可用集群 keepalived高可用主机IP:172.21.5.22和172.21.5.21 http服务高可用主机IP:172.21.5.16和172.21.5.18 VIP采用172.16.32.5 各虚拟机及主机名和IP对应关系如下所示: 虚拟机主机…...
如何使新手小白编码能力暴涨之Devchat-AI
在这个快速发展的时代,开发者的任务越来越繁重,要求他们快速、高效地完成开发任务。然而,传统的开发方式已经无法满足这个需求。在这种情况下,Devchat的出现给开发者带来了新的帮助。Devchat是一个研发效能分析平台,它…...
SAP ABAP基础语法-TCODE学习(八)
一、 SD-如何在订单中使用客户层次定价的配置和维护步骤 在SD中有时会用到按客户层次进行定价的策略,我这里就将配置和维护的步骤简单写出来,供大家参考. 1)定义层次类型(VOH1) 路径:销售和分销->主数据->业务合作伙伴->客户->客户层次->定义层次类型 (1)伙…...
stm32-arm固件开发
文章目录 前言1. 前言 ARM体系结构与程序设计【全68讲】 1....
LeetCode 面试题 16.17. 连续数列
文章目录 一、题目二、C# 题解 一、题目 给定一个整数数组,找出总和最大的连续数列,并返回总和。 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。…...
基于人工蜂鸟算法的无人机航迹规划-附代码
基于人工蜂鸟算法的无人机航迹规划 文章目录 基于人工蜂鸟算法的无人机航迹规划1.人工蜂鸟搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用人工蜂鸟算法来优化无人机航迹规划。 …...
51单片机汇编-点亮一个led
文章目录 前言1.打开IDE2.设置编辑器3.设置输出4. 原理图5.编写代码6 编译7.下载8.其它代码1.LED闪烁2.跑马灯 前言 51单片机基础 51汇编实战 本章主要介绍打开一个led,具体采用51汇编 1.打开IDE 选择STC89C52RC 后缀是.asm 2.设置编辑器 3.设置输出 4. 原理图 5.编写代码 …...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
