C++类和对象(下)

发布时间:2026/9/20 1:56:19

C++类和对象(下) 目录再探构造函数类型转换static成员友元内部类匿名对象对象拷贝时的编译器优化再探构造函数•之前我们实现构造函数时初始化成员变量主要使用函数体内赋值构造函数初始化还有⼀种方式就是初始化列表初始化列表的使用方式是以⼀个冒号开始接着是⼀个以逗号分隔的数据成员列表每个成员变量后面跟⼀个放在括号中的初始值或表达式。•每个成员变量在初始化列表中只能出现⼀次语法理解上初始化列表可以认为是每个成员变量定义初始化的地⽅。•引⽤成员变量const成员变量没有默认构造的类类型变量必须放在初始化列表位置进行初始化否则会编译报错。•C11⽀持在成员变量声明的位置给缺省值这个缺省值主要是给没有显示在初始化列表初始化的 成员使用的。•尽量使用初始化列表初始化因为那些你不在初始化列表初始化的成员也会⾛初始化列表如果这 个成员在声明位置给了缺省值初始化列表会用这个缺省值初始化。如果你没有给缺省值对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器C并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调⽤这个成员类型的默认构造函数如果没有默认构造会编译错误。•初始化列表中按照成员变量在类中声明顺序进行初始化跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。初始化列表总结无论是否显示写初始化列表每个构造函数都有初始化列表无论是否在初始化列表显示初始化成员变量每个成员变量都要走初始化列表初始化#includeiostream using namespace std; class Time { public: Time(int hour) :_hour(hour) { cout Time() endl; } private: int _hour; }; class Date { public: Date(int x, int year 1, int month 1, int day 1) :_year(year) ,_month(month) ,_day(day) ,_t(12) ,_ref(x) ,_n(1) { // error C2512: Time: 没有合适的默认构造函数可用 // error C2530: Date::_ref: 必须初始化引用 // error C2789: Date::_n: 必须初始化常量限定类型的对象 } void Print() const { cout _year - _month - _day endl; } private: int _year; int _month; int _day; Time _t; // 没有默认构造 int _ref; // 引用 const int _n; // const }; int main() { int i 0; Date d1(i); d1.Print(); return 0; }#includeiostream using namespace std; class Time { public: Time(int hour) :_hour(hour) { cout Time() endl; } private: int _hour; }; class Date { public: Date() :_month(2) { cout Date() endl; } void Print() const { cout _year - _month - _day endl; } private: // 注意这里不是初始化这里给的是缺省值这个缺省值是给初始化列表的 // 如果初始化列表没有显示初始化默认就会用这个缺省值初始化 int _year 1; int _month 1; int _day; Time _t 1; const int _n 1; int* _ptr (int*)malloc(12); }; int main() { Date d1; d1.Print(); return 0; }类型转换•C 支持内置类型隐式类型转换为类类型对象需要有相关内置类型为参数的构造函数。•构造函数前面加explicit就不再支持隐式类型转换。•类类型的对象之间也可以隐式转换需要相应的构造函数支持。#includeiostream using namespace std; class A { public: // 构造函数explicit就不再支持隐式类型转换 // explicit A(int a1) A(int a1) :_a1(a1) {} //explicit A(int a1, int a2) A(int a1, int a2) :_a1(a1) ,_a2(a2) {} void Print() { cout _a1 _a2 endl; } int Get() const { return _a1 _a2; } private: int _a1 1; int _a2 2; }; class B { public: B(const A a) :_b(a.Get()) {} private: int _b 0; }; int main() { // 1构造一个A的临时对象再用这个临时对象拷贝构造aa1 // 编译器遇到连续构造拷贝构造-优化为直接构造 A aa1 1; aa1.Print(); const A aa2 1; // C11之后才支持多参数转化 A aa3 { 2,2 }; // aa3隐式类型转换为b对象 // 原理跟上面类似 B b aa3; const B rb aa3; return 0; }static成员•用static修饰的成员变量称之为静态成员变量静态成员变量⼀定要在类外进行初始化。•静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。•用static修饰的成员函数称之为静态成员函数静态成员函数没有this指针。•静态成员函数中可以访问其他的静态成员但是不能访问⾮静态的因为没有this指针。•非静态的成员函数可以访问任意的静态成员变量和静态成员函数。•突破类域就可以访问静态成员可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。•静态成员也是类的成员受public、protected、private 访问限定符的限制。•静态成员变量不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的静态成员变量不属于某个对象不走构造函数初始化列表。#includeiostream using namespace std; // 实现一个类计算程序中创建出了多少个类对象 class A { public: A() { _scount; } A(const A t) { _scount; } ~A() { --_scount; } static int GetACount() { return _scount; } private: // 类里面声明 static int _scount; }; // 类外面初始化 int A::_scount 0; int main() { cout A::GetACount() endl; A a1, a2; A a3(a1); cout A::GetACount() endl; cout a1.GetACount() endl; // 编译报错: error C2248: A::_scount: 无法访问 private 成员(在“A”类中声明) //cout A::_scount endl; return 0; }友元•友元提供了⼀种突破类访问限定符封装的方式友元分为友元函数和友元类在函数声明或者类声明的前⾯加friend并且把友元声明放到⼀个类的里面。•外部友元函数可访问类的私有和保护成员友元函数仅仅是⼀种声明他不是类的成员函数。•友元函数可以在类定义的任何地方声明不受类访问限定符限制。•⼀个函数可以是多个类的友元函数。•友元类中的成员函数都可以是另⼀个类的友元函数都可以访问另⼀个类中的私有和保护成员。•友元类的关系是单向的不具有交换性比如A类是B类的友元但是B类不是A类的友元。•友元类关系不能传递如果A是B的友元 B是C的友元但是A不是C的友元。•有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多⽤。#includeiostream using namespace std; // 前置声明否则A的友元函数声明编译器不认识B class B; class A { // 友元声明 friend void func(const A aa, const B bb); private: int _a1 1; int _a2 2; }; class B { // 友元声明 friend void func(const A aa, const B bb); private: int _b1 3; int _b2 4; }; void func(const A aa, const B bb) { cout aa._a1 endl; cout bb._b1 endl; } int main() { A aa; B bb; func(aa, bb); return 0; } #includeiostream using namespace std; class A { // 友元声明 friend class B; private: int _a1 1; int _a2 2; }; class B { public: void func1(const A aa) { cout aa._a1 endl; cout _b1 endl; } void func2(const A aa) { cout aa._a2 endl; cout _b2 endl; } private: int _b1 3; int _b2 4; }; int main() { A aa; B bb; bb.func1(aa); bb.func1(aa); return 0; }内部类•如果⼀个类定义在另⼀个类的内部这个内部类就叫做内部类。内部类是⼀个独立的类跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。•内部类默认是外部类的友元类。•内部类本质也是⼀种封装当A类跟B类紧密关联A类实现出来主要就是给B类使用那么可以考虑把A类设计为B的内部类如果放到private/protected位置那么A类就是B类的专属内部类其 他地方都用不了。#includeiostream using namespace std; class A { private: static int _k; int _h 1; public: class B // B默认就是A的友元 { public: void foo(const A a) { cout _k endl; //OK cout a._h endl; //OK } int _b1; }; }; int A::_k 1; int main() { cout sizeof(A) endl; A::B b; A aa; b.foo(aa); return 0; }匿名对象•用类型(实参) 定义出来的对象叫做匿名对象相比之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象•匿名对象生命周期只在当前⼀行⼀般临时定义⼀个对象当前用⼀下即可就可以定义匿名对象。#includeiostream using namespace std; class A { public: A(int a 0) :_a(a) { cout A(int a) endl; } ~A() { cout ~A() endl; } private: int _a; }; class Solution { public: int Sum_Solution(int n) { //... return n; } }; int main() { A aa1; // 不能这么定义对象因为编译器无法识别下面是一个函数声明还是对象定义 //A aa1(); // 但是我们可以这么定义匿名对象匿名对象的特点不取名字 // 但是他的生命周期只有这一行我们可以看到下一行他就会自动调用析构函数 A(); A(1); A aa2(2); // 匿名对象在这样场景下就很好用当然还有一些其他使用场景这个我们以后遇到了再说 Solution().Sum_Solution(10); return 0; }对象拷贝时的编译器优化•现代编译器会为了尽可能提高程序的效率在不影响正确性的情况下会尽可能减少⼀些传参和传返回值的过程中可以省略的拷贝。•如何优化C标准并没有严格规定各个编译器会根据情况自行处理。当前主流的相对新⼀点的编译器对于连续⼀个表达式步骤中的连续拷贝会进⾏合并优化有些更新更激进的编译器还会进⾏跨行跨表达式的合并优化。•linux下可以将下面代码拷贝到test.cpp⽂件编译时⽤ g test.cpp -fno-elide-constructors 的方式关闭构造相关的优化。#includeiostream using namespace std; class A { public: A(int a 0) :_a1(a) { cout A(int a) endl; } A(const A aa) :_a1(aa._a1) { cout A(const A aa) endl; } A operator(const A aa) { cout A operator(const A aa) endl; if (this ! aa) { _a1 aa._a1; } return *this; } ~A() { cout ~A() endl; } private: int _a1 1; }; void f1(A aa) {} A f2() { A aa; return aa; } int main() { // 传值传参 // 构造拷贝构造 A aa1; f1(aa1); cout endl; // 隐式类型连续构造拷贝构造-优化为直接构造 f1(1); // 一个表达式中连续构造拷贝构造-优化为一个构造 f1(A(2)); cout endl; cout **************************************** endl; // 传值返回 // 不优化的情况下传值返回编译器会生成一个拷贝返回对象的临时对象作为函数调用表达式的返回值 // 无优化vs2019 debug // 一些编译器会优化得更厉害将构造的局部对象和拷贝构造的临时对象优化为直接构造。(vs2022 debug) f2(); cout endl; // 返回时一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造。(vs2019 debug) // 一些编译器会优化得更厉害进行跨行合并优化将构造的局部对象aa和拷贝的临时对象和接收返回值对象aa2优化为一个直接构造。(vs2022 debug) A aa2 f2(); cout endl; // 一个表达式中开始构造中间拷贝构造赋值重载-无法优化(vs2019 debug) // 一些编译器会优化得更厉害进行跨行合并优化将构造的局部对象aa和拷贝临时对象合并为一个直接构造(vs2022 debug) aa1 f2(); cout endl; return 0; }
延伸阅读

更多相关文章

2026/9/20 1:56:19

棋盘游戏建模:二分图最大匹配算法详解与实现

1. 从棋盘到图论:一个经典问题的建模思路很多朋友第一次接触“棋盘游戏”这个题目时,可能会有点懵:棋盘游戏和二分图最大匹配有什么关系?这其实是算法竞赛和面试中一个非常经典的建模问题,它完美地展示了如何将一个看似…

2026/9/20 1:56:22

通信复用技术全解析:从FDM、TDM到WDM、CDMA与SDM

1. 从一根电话线说起:为什么我们需要复用技术?如果你用过老式的固定电话,可能会记得一个场景:一根细细的电话线从墙里伸出来,连接到你的座机上。在很长一段时间里,这根线似乎只负责一件事——传输你和远方亲…

2026/9/20 21:21:50

Phoenix Contexts 入门:用 Elixir 模块封装数据访问与业务逻辑

Phoenix Contexts 入门:用 Elixir 模块封装数据访问与业务逻辑 【免费下载链接】phoenix Peace of mind from prototype to production 项目地址: https://gitcode.com/gh_mirrors/ph/phoenix 本指南介绍 Phoenix 框架的核心设计概念 —— Contexts&#xff…

2026/9/20 21:21:50

Atlas 300V AI推理加速卡:YOLO模型从零部署与性能调优实战

1. 这块“运算加速卡”,到底解决了什么问题先给你吃一颗定心丸:Atlas 300V 24G 不是我们常说的那种跑训练的大显卡,它是一张面向推理场景的专用加速卡,官方定位是数据中心和边缘侧的人工智能推理。很多人第一次看到“24G”这个数字…

2026/9/20 21:21:50

ComfyUI 工作流中文合集:16 个预设模板,导入即可出图

ComfyUI 工作流中文合集:16 个预设模板,导入即可出图 【免费下载链接】ComfyUI-Workflows-ZHO 我的 ComfyUI 工作流合集 | My ComfyUI workflows collection 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-Workflows-ZHO ComfyUI-Wo…

2026/9/20 21:16:50

Pandoc 3.6.4实战:Markdown与Word/PDF高效转换指南

简介:Pandoc 是广为使用的开源文档转换利器,支持 Markdown、HTML、LaTeX、Word DOCX、EPUB 等数十种标记格式之间的相互转换,在写作、出版、学术排版与技术文档维护中非常实用,尤其适合需要批量处理文档格式的开发者、技术写作者和…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/20 5:01:23

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/20 5:09:33

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码