四、类和对象(三)

👉 欢迎阅读这篇文章 👇

目录

  • 1、构造函数延伸
  • 2、类型转换
  • 3、static成员
  • 4、友元
  • 5、内部类
  • 6、匿名对象
  • 7、对象拷贝时的编译器优化

1、构造函数延伸

我们上一篇文章中介绍了构造函数,我们在实现构造函数的时候,初始化成员变量主要使用函数体赋值,构造函数的初始化还有另一种方式——初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分割的数据成员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。

classDate{public:Date(intyear=1,intmonth=1,intday=1):_year(year),_month(month),_day(day){}private:int_year;int_month;int_day;};
  • 每个成员变量在初始化列表中只能出现一次,在语法上可以将初始化列表理解为每个成员变量定义初始化的地方。
  • 引用成员变量、const成员变量、没有默认构造的类类型变量,必须放在初始化位置进行初始化。(可以理解为这三个定义和初始化要同时进行,所以只能在初始化列表定义的同时就初始化了)
classTime{public://没有默认构造函数Time(inthour):_hour(hour){cout<<"Time()"<<endl;}private:int_hour;};classDate{public:Date(int&x,intyear=1,intmonth=1,intday=1):_year(year),_month(month),_day(day),_ref(x),_t(12),_n(1){// error C2512: “Time”: 没有合适的默认构造函数可⽤// error C2530 : “Date::_ref” : 必须初始化引⽤// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象}private://声明int_year;int_month;int_day;//下面这三个可以理解为定义和初始化要同时进行,所以只能在初始化列表定义的同时就初始化了int&_ref;//引用Time _t;//没有默认构造的类类型变量constint_n;//const成员变量};
  • C++11支持在成员变量声明的时候,给缺省值,这个缺省值主要是给没有显式在初始化列表初始化的成员使用的。
  • 尽量使用初始化列表初始化,因为那些不在初始化列表初始化的成员,如果这个成员声明的时候给了缺省值,那么初始化列表就会用这个缺省值初始化。
  • 如果声明的时候没有给缺省值,而且没有显式在初始化列表初始化的成员
    • 对于内置类型成员,是否初始化取决于编译器,C++没有规定。
    • 对于自定义类类型成员,会调用这个成员类型的默认构造函数,如果没有默认构造函数,就会报错。
classTime{public://没有默认构造函数Time(inthour):_hour(hour){cout<<"Time()"<<endl;}private:int_hour;};classDate{public:Date(int&x)//成员声明的时候给了缺省值,那么初始化列表就会用这个缺省值初始化。:_ref(x),_t(12){cout<<"Date(int& x)"<<endl;}private://声明int_year=2026;//给缺省值int_month=10;//给缺省值int_day;//没有给缺省值,而且没有显式在初始化列表初始化的成员,内置类型成员,是否初始化取决于编译器,C++没有规定。//下面这三个可以理解为定义和初始化要同时进行,所以只能在初始化列表定义的同时就初始化了int&_ref;//引用——必须在初始化列表中初始化//Time _t;//没有默认构造的类类型变量——没有给缺省值,而且没有显式在初始化列表初始化的成员,自定义类型成员,会调用它的默认构造。——没有默认构造,报错Time _t;//没有默认构造的类类型变量——没有给缺省值,但是显式在初始化列表初始化的成员,会在初始化列表中初始化。constint_n=10;//const成员变量,声明给缺省值};intmain(){intx=0;Dated1(x);return0;}

初始化列表总结

  • ⽆论是否显式写初始化列表,每个构造函数都有初始化列表;
  • ⽆论是否在初始化列表显式初始化成员变量,每个成员变量都要⾛初始化列表初始化;
  • 成员变量走初始化列表的步骤(如下图)

初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆关。建议声明顺序和初始化列表顺序保持⼀致。

练习

下⾯程序的运⾏结果是什么()
A. 输出 1 1
B. 输出 2 2
C. 编译报错
D. 输出 1 随机值
E. 输出 1 2
F. 输出 2 1

classA{public:A(inta):_a1(a),_a2(_a1){}voidPrint(){cout<<_a1<<" "<<_a2<<endl;}private:int_a2=2;int_a1=2;};intmain(){Aaa(1);aa.Print();}

已知初始化列表中按照成员变量在类中声明顺序进⾏初始化

那么就会先初始化_a2,在初始化列表中显式写了,那么就按初始化列表中给的值初始化,给了_a1,这时_a1还未进行初始化,是随机值,所以_a2就是随机值。

再初始化_a1,在初始化列表中显式写了,那么就按初始化列表中给的值初始化,给了1,所以_a1就是1。

输出1、随机值。

通过上方的练习题我们就可以感受到如果声明顺序和初始化列表顺序不保持⼀致,那么就有可能出现随机值。

2、类型转换

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

构造函数前⾯加explicit就不再⽀持隐式类型转换。

classA{public://以内置类型为参数的拷贝构造//构造函数前加explicit就不再支持隐式转换//explicit A(int a)A(inta):_a1(a){}A(inta1,inta2):_a1(a1),_a2(a2){}voidPrint(){cout<<_a1<<endl;}private:int_a1;int_a2;};classArray{public:voidPushBack(constA&a){}};intmain(){//构造Aaa1(2);aa1.Print();//类型转换//语法逻辑上:构造(用内置类型2进行构造出临时对象)+拷贝构造+优化->构造A aa2=2;aa2.Print();//引用的是A类型的临时变量,要const引用constA&aa3=1;//使用场景Array arr;constA&aa4=2;arr.PushBack(aa4);//传参更方便(内置类型“2”隐式转换成自定义类型A类型的变量传参给PushBack)arr.PushBack(2);//构造Aaa5(2,3);//多个参数的类型转换,将内置类型用{}括起来A aa6={1,2};constA&aa7={1,2};Array arr2;arr2.PushBack(aa7);arr2.PushBack({2,3});}

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

classA{public://以内置类型为参数的拷贝构造//构造函数前加explicit就不再支持隐式转换//explicit A(int a)A(inta):_a1(a){}A(inta1,inta2):_a1(a1),_a2(a2){}voidPrint(){cout<<_a1<<endl;}intGet()const{return_a1+_a2;}private:int_a1;int_a2;};classB{public://构造函数——将A类型对象a的a1和a2之和拷贝给B类型——用于支持类与类之间的构造B(constA&a):_b(a.Get())//a是const A*类型的,所以Get需要是const成员函数{}private:int_b;};intmain(){//构造Aaa1(2);aa1.Print();//用aa1构造出B类型临时对象,拷贝给bb1B bb1=aa1;constB&bb2=aa1;}

3、static成员

用static修饰的成员变量,称为静态成员变量,静态成员变量一定要在类外进行初始化。

静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。

用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。

静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针。

非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。

突破类域可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。

静态成员也是类的成员,受public、protected、private 访问限定符的限制。

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

classA{public://以内置类型为参数的构造函数//构造函数前加explicit就不再支持隐式转换//explicit A(int a)A(inta):_a1(a){}A(inta1,inta2):_a1(a1),_a2(a2){}voidPrint(){cout<<_a1<<endl;}staticintGetcount(){//return _a1 + _a2; static修饰的成员函数没有this指针,不可以访问非静态成员return_scount;//static静态成员函数可以访问其他静态成员,不可以访问非静态成员}intGet()const{//return _scount;returnGetcount();}private:int_a1;int_a2;public:staticint_scount;//声明静态成员变量};intA::_scount=2;//静态成员变量要在类外初始化intmain(){Aaa1(1);A aa2=2;cout<<A::_scount<<endl;//突破类域访问静态成员变量cout<<aa1._scount<<endl;cout<<A::Getcount()<<endl;//突破类域访问静态成员函数cout<<aa1.Getcount()<<endl;cout<<aa1.Get()<<endl;//非静态成员函数可以访问静态成员和静态成员函数return0;}
// 实现一个类,计算程序中创建出了多少个类对象?classA{public://构造函数A(){++_scount;}//拷贝构造A(constA&a1){++_scount;}//析构~A(){--_scount;}staticintGetACount(){return_scount;}private:staticint_scount;};intA::_scount=0;intmain(){cout<<A::GetACount()<<endl;A a1,a2;Aa3(a1);cout<<A::GetACount()<<endl;cout<<a1.GetACount()<<endl;return0;}

求1+2+3+…n

classsum{public:sum(){_ret+=i;i++;}staticintGetret(){return_ret;}private:staticint_ret;staticinti;};intsum::_ret=0;intsum::i=1;classSolution{public:intSum_Solution(intn){sum arr[n];//变长数组,实例化n次,执行n次构造函数,构造函数哪进行相加returnsum::Getret();}};

4、友元

友元提供了一种突破类访问限定符封装的方式,友元分为友元函数和友元类。

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

外部友元函数可访问类的私有和保护成员,友元函数仅仅是类的一种声明,不是类的成员函数。

友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。

一个函数可以是多个类的友元函数。

友元类中的成员函数可以是另一个类的友元函数,可以访问另一个类中的保护和私有成员。

友元类的关系是单向的,不具有交换性,比如A是B的友元,但是B不是A的友元。

友元类的关系不能传递如果A是B的友元, B是C的友元,但是A不是C的友元。

有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。

5、内部类

如果一个类定义在另一个类的内部,这个类就是内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制访问限定符限制,所以外部类定义的对象中不包含内部类。

内部类默认是外部类的友元类。

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

//内部类classA{private:staticint_k;int_h=1;public:classB{public:voidfoo(constA&a){cout<<_k<<endl;//OKcout<<a._h<<endl;//OK}int_b1;};};intA::_k=0;intmain(){cout<<sizeof(A)<<endl;A::B b;A aa;b.foo(aa);return0;}

求1+2+3+…n

//改造classSolution{public:classsum{public:sum(){_ret+=i;i++;}staticint_ret;staticinti;};intSum_Solution(intn){sum arr[n];//变长数组,实例化n次,执行n次构造函数,构造函数哪进行相加returnsum::_ret;}};intsum::_ret=0;intsum::i=1;

6、匿名对象

用类型(实参)定义出来的对象叫匿名对象。

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

//匿名对象classA{public:A(inta=0):_a(a){cout<<"A(int a)"<<endl;}~A(){cout<<"~A()"<<endl;}private:int_a;};classSolution{public:intSum_Solution(intn){//...returnn;}};voidPushBack(A aa){}intmain(){A aa1;//匿名对象,生命周期只有这一行,可以观察会进行析构A();A(1);//可以传参数构造Solution so;so.Sum_Solution(1);Solution().Sum_Solution(2);//利用匿名对象可以直接这样使用Aaa3(3);PushBack(aa3);PushBack(A(3));//与上方等价return0;}

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

现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返回值的过程中可以省略的拷⻉。

如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。

当前主流的相对新⼀点的编译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译器还会进⾏跨⾏跨表达式的合并优化。

在接收返回值的时候,尽量用拷贝构造接收返回值,而不是赋值重载,这样编译器有更大的空间去优化。

相关新闻

4款芯片获UFCS认证,快充协议碎片化时代迎来破局

4款芯片获UFCS认证,快充协议碎片化时代迎来破局

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 2:12:24 阅读更多 →
十分钟教你如何升级OpenClaw:npm与Docker两种方式配TaoToken

十分钟教你如何升级OpenClaw:npm与Docker两种方式配TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 2:11:22 阅读更多 →
Move语言的区块链:从0到1构建资源导向的安全智能合约

Move语言的区块链:从0到1构建资源导向的安全智能合约

引言 区块链技术自比特币白皮书发布以来,已经彻底改变了我们对价值转移、数据存储和信任机制的理解。在智能合约语言的发展历程中,Move语言代表了一次根本性的范式转移。它最初由Meta(原Facebook)的Diem团队开发,虽然Diem项目最终被放弃,但Move语言却在Aptos、Sui等新一…

2026/10/20 2:11:22 阅读更多 →

最新新闻

大模型Skills应用实战:在Trae中把提示词变成可复用工作流

大模型Skills应用实战:在Trae中把提示词变成可复用工作流

这个系列写到第14篇&#xff0c;我聊点真正能提效的东西——大模型Skills应用。之前几篇要么在讲模型能力边界&#xff0c;要么在讲上下文工程&#xff0c;这次把视角落到一个具体场景&#xff1a;在Trae这款AI原生IDE里&#xff0c;怎么把Skill从"听说过"变成"…

2026/10/20 2:53:45 阅读更多 →
PC端缩放布局不崩:clamp()、视口字号、防撑破与Grid弹性收缩

PC端缩放布局不崩:clamp()、视口字号、防撑破与Grid弹性收缩

做 Web 开发的人&#xff0c;十有八九都被一个场景折腾过&#xff1a;你在 PC 上写了个布局&#xff0c;看着还挺满意。结果用户那边把浏览器窗口拉窄一点&#xff0c;或者按了Ctrl 滚轮缩放一下&#xff0c;整个页面瞬间就没法看了——侧边栏把内容挤成一团、文字溢出到屏幕外…

2026/10/20 2:53:45 阅读更多 →
OpenCode技能机制详解:从SKILL.md设计到团队共享实战

OpenCode技能机制详解:从SKILL.md设计到团队共享实战

1. 这份折腾的前提&#xff1a;OpenCode是什么&#xff0c;技能解决的是什么问题先说清楚我为什么会去研究这个&#xff0c;因为市面上叫"技能"或"Skills"的AI功能太多了。几个月前我还在终端里同时开着几个不同的AI编程工具&#xff0c;来回粘贴代码上下文…

2026/10/20 2:53:45 阅读更多 →
基于YOLOv5的动态增强遥感小目标检测方案

基于YOLOv5的动态增强遥感小目标检测方案

做遥感图像目标检测的时候&#xff0c;有一类问题特别让人头疼&#xff1a;高分辨率遥感图里目标很小、分布密集&#xff0c;通用检测模型跑起来一看&#xff0c;漏检的全是那些只有几十像素甚至十几个像素的小目标。我最初直接用现成的一套检测框架去处理&#xff0c;效果始终…

2026/10/20 2:53:45 阅读更多 →
BEV感知数据工程:数据集选择、标定时序与真值对齐

BEV感知数据工程:数据集选择、标定时序与真值对齐

做BEV感知这块时间久了&#xff0c;会有个特别深的感受&#xff1a;模型结构只是面子&#xff0c;数据工程才是里子。我接触过的几个环视检测项目里&#xff0c;模型选型、backbone替换、多尺度融合&#xff0c;加起来可能只花一周&#xff1b;真正耗进去一个多月的&#xff0c…

2026/10/20 2:53:45 阅读更多 →
Spring Boot公益服务平台开发实战:从数据库建模到答辩全流程指南

Spring Boot公益服务平台开发实战:从数据库建模到答辩全流程指南

最近帮几个学弟学妹把基于Spring Boot的公益服务平台从零到一完整搭了一遍&#xff0c;整个过程走了不少弯路&#xff0c;也沉淀了不少可以直接复用的经验。这类系统在课程设计和毕业设计里出现频率很高&#xff0c;一是业务场景贴近生活&#xff0c;二是功能边界清晰——用户注…

2026/10/20 2:52:44 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →