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.编写代码 …...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
