C++ 类和对象(下):从构造函数到编译器优化
本篇博客将结合一份完整的 C 代码实例涵盖 Date 类、Stack 类、内部类等详细剖析类和对象的各个核心知识点包括构造函数、初始化列表、拷贝控制、运算符重载、static 成员、友元、内部类、匿名对象以及编译器优化等。同时会补充代码中未直接体现但必须掌握的扩展内容力求做到面面俱到。1. 类的基本结构封装与成员C 类通过class或struct定义将数据成员变量和操作成员函数封装在一起。访问限定符public、protected、private控制外部对成员的访问权限。在提供的代码中Date类是一个典型的日期类包含了年、月、日三个私有成员变量以及公有的构造函数、拷贝构造函数、赋值运算符、析构函数、各种运算符重载等。class Date { private: int _year; int _month; int _day; public: // 各种成员函数 };2. 构造函数与初始化列表2.1 构造函数的重载与默认参数构造函数在对象创建时自动调用用于初始化对象。可以重载多个构造函数也可以使用默认参数来提供灵活的参数传递方式。Date(int year 2026, int month 7, int day 23) { _year year; _month month; _day day; }以上构造函数带有三个默认参数因此可以以多种方式调用Date d1;→ 使用全部默认值 2026/7/23Date d2(2025);→ 年2025月、日使用默认 7/23Date d3(2025, 4);→ 年2025月4日默认 23Date d4(2025,4,24);→ 全部显式指定注意如果用户定义了任何构造函数编译器不再自动生成默认构造函数。但这里提供了全缺省构造函数因此它本身就是默认构造函数不需要参数就能调用。2.2 初始化列表Initializer List初始化列表是在构造函数参数列表之后、函数体之前以冒号:开始后面跟以逗号分隔的“成员变量(初始值)”的列表。它是成员变量真正初始化的地方而函数体中的赋值只是赋值操作而非初始化。为什么必须使用初始化列表有些类型的成员变量必须在定义时初始化不能在构造函数体内赋值引用成员变量int _ref;const成员变量const int _n;没有默认构造函数的类类型成员Time _t;且Time没有默认构造函数上述三种情况必须在初始化列表中显式初始化否则编译报错。class Time { public: Time(int hour) : _hour(hour) {} // 无默认构造 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) // const 必须初始化 {} private: int _year; int _month; int _day; Time _t; int _ref; const int _n; };初始化列表的执行顺序按照成员变量在类中的声明顺序进行初始化而不是按照初始化列表中的顺序。这一点容易出错建议保持声明顺序与初始化列表顺序一致。缺省值C11可以在成员变量声明处直接给缺省值这些值会被用于初始化列表如果该成员在初始化列表中未出现。例如int _year 1; int _month 1; int _day 1;如果构造函数初始化列表中没有显式写出_year则编译器会用缺省值 1 来初始化它。初始化列表总结每个构造函数都有初始化列表即使不写编译器也会生成。每个成员变量都会走初始化列表要么显式指定要么使用缺省值要么默认初始化。显式初始化的成员按给定值初始化未显式初始化但有缺省值的用缺省值都没有的内置类型可能未初始化随机值自定义类型调用默认构造函数若无则编译错误。2.3 explicit 关键字与隐式类型转换C 允许通过构造函数进行隐式类型转换如果构造函数只有一个参数或者除第一个参数外都有默认值那么该构造函数可以用于将参数类型隐式转换为类类型。class A { public: A(int a) : _a(a) {} // ... }; A aa 10; // 隐式转换先构造临时 A(10)再拷贝构造 aa编译器通常会优化为直接构造若想禁止这种隐式转换可以在构造函数前加explicitexplicit A(int a) : _a(a) {} A aa 10; // 编译错误代码还展示了 C11 支持的多参数隐式转换使用花括号A aa {1, 2}; // 需要构造函数 A(int, int) const A ref {1, 2}; // 绑定到临时对象3. 拷贝控制拷贝构造、赋值运算符、析构函数3.1 拷贝构造函数拷贝构造函数用于使用一个同类型对象初始化另一个对象。其参数必须是本类类型的引用通常为 const 引用否则会无限递归因为传值本身需要拷贝。Date(const Date d) { _year d._year; _month d._month; _day d._day; }何时调用拷贝构造用一个对象初始化另一个对象Date d2(d1);或Date d2 d1;函数参数按值传递void func(Date d);调用时实参拷贝给形参。函数返回对象按值返回Date func() { Date d; return d; }返回时拷贝给临时对象可能被优化。深拷贝 vs 浅拷贝当类管理动态资源时如Stack类中的_a指针默认的拷贝构造函数只进行浅拷贝指针值复制导致两个对象指向同一块内存析构时 double free。因此必须显式定义拷贝构造进行深拷贝分配新内存并复制内容。Stack(const Stack s) { _a (STDataType*)malloc(sizeof(STDataType) * s._capacity); memcpy(_a, s._a, sizeof(STDataType) * s._top); _top s._top; _capacity s._capacity; }3.2 赋值运算符重载赋值运算符用于两个已经存在的对象之间的赋值。Date operator(const Date d) { if (this ! d) // 防止自赋值 { _year d._year; _month d._month; _day d._day; } return *this; // 返回左侧对象的引用支持连续赋值 }与拷贝构造的区别拷贝构造是“从无到有”赋值是“已有到已有”。3.3 析构函数析构函数在对象生命周期结束时自动调用用于释放资源。名称是~类名无参数无返回值。~Date() { cout ~Date() endl; }对于Date类没有动态资源析构函数可以不写编译器默认的足够。但对于Stack类必须释放_a指向的内存。~Stack() { free(_a); _a nullptr; _top _capacity 0; }析构顺序先构造的后析构与构造顺序相反。在包含对象成员时先执行类自己的析构体然后成员对象按声明顺序的逆序析构。4. 运算符重载C 允许为自定义类型重载运算符使对象支持与内置类型相似的语法。4.1 比较运算符代码中实现了、!、、、、。其中和作为基础其他复用它们。bool operator(const Date d) { return _year d._year _month d._month _day d._day; } bool operator!(const Date d) { return !(*this d); } bool operator(const Date d) { /* 依次比较年月日 */ } bool operator(const Date d) { return *this d || *this d; } bool operator(const Date d) { return !(*this d); } bool operator(const Date d) { return !(*this d); }注意比较运算符通常定义为类的成员函数也可以定义为友元全局函数。4.2 算术运算符、-、、-为了支持日期加减天数实现了operator、operator-、operator、operator-。它们的实现利用了辅助函数GetMonthDay来获取每月的天数考虑闰年。 实现先检查负数天数转换为减法否则将天数加到_day然后循环进位。Date operator(int day) { if (day 0) return *this - -day; _day day; while (_day GetMonthDay(_year, _month)) { _day - GetMonthDay(_year, _month); _month; if (_month 13) { _month 1; _year; } } return *this; } 实现利用拷贝构造一个临时对象对其调用然后返回临时对象拷贝或移动。Date operator(int day) { Date tmp(*this); tmp day; return tmp; }类似地-和-也有对应实现。4.3 自增自减运算符前置与后置前置d和d有区别后置需要带一个哑元参数int来区分。// 前置 Date operator() { *this 1; return *this; } // 后置 Date operator(int) // int 参数无实际用途仅用于区分 { Date tmp(*this); *this 1; return tmp; }类似实现--前置和后置。4.4 流插入和提取运算符 和 由于和的左操作数是ostream/istream对象不能作为成员函数成员函数的左操作数是类对象本身所以必须定义为全局函数。为了访问私有成员声明为友元。friend ostream operator(ostream out, const Date d); friend istream operator(istream in, Date d);实现时直接访问私有变量。operator还包含循环验证日期合法性。ostream operator(ostream out, const Date d) { out d._year 年 d._month 月 d._day 日; return out; } istream operator(istream in, Date d) { // 输入并校验 return in; }5. static 成员静态成员5.1 静态成员变量用static修饰的成员变量属于类不属于某个对象。所有对象共享同一个静态成员变量。它存储在静态存储区必须在类外单独定义初始化。class A { private: static int _scount; // 声明 }; int A::_scount 0; // 定义并初始化只能在类外静态成员变量不能在类内声明时给缺省值因为它不是某个对象的成员不参与构造初始化列表。5.2 静态成员函数静态成员函数没有this指针因此只能访问静态成员变量/函数不能访问非静态成员。static int GetACount() { return _scount; }调用方式A::GetACount()或通过对象a.GetACount()但对象只是提供了类域本质上还是类级别的调用。应用场景计数对象个数、单例模式等。代码中利用静态成员和内部类实现了“求 12...n”的巧妙解法利用构造函数累加。class Sum { public: Sum() { _ret _i; _i; } static int GetRet() { return _ret; } private: static int _i; static int _ret; }; int Sum::_i 1; int Sum::_ret 0; // 然后在 Solution 中创建 Sum 数组触发 n 次构造函数完成累加。6. 友元friend友元提供了一种打破封装的机制允许外部函数或类访问私有成员。但应谨慎使用以免破坏封装性。6.1 友元函数在类内声明一个全局函数为友元该函数即可访问该类的私有成员。class A { friend void func(const A, const B); // 声明 func 为友元 private: int _a1; };6.2 友元类若 A 类声明 B 类为友元则 B 的所有成员函数都可以访问 A 的私有成员。class A { friend class B; // B 是 A 的友元 private: int _a; }; class B { void accessA(const A a) { cout a._a; } // 合法 };友元关系是单向的A 是 B 的友元不代表 B 是 A 的友元且不可传递。7. 内部类Nested Class内部类是定义在另一个类内部的类。它是一个独立的类只是受到外部类的类域和访问限定符的限制。外部类定义的对象中不包含内部类的对象内部类只是类型定义。特性内部类默认是外部类的友元可以访问外部类的私有成员。内部类可以访问外部类的静态成员无论它们是否为私有。内部类通常在外部类的作用域内使用可以通过外部类::内部类创建对象。示例class A { private: static int _k; public: class B { // 默认是 A 的友元 public: void foo(const A a) { cout _k; // 访问外部类的静态私有成员 cout a._h; // 访问外部类的非静态私有成员因为友元 } }; };内部类常用于实现“专属类”如牛客网的“求 1...n”解法中将Sum放在Solution内部作为私有内部类外界无法使用。8. 匿名对象临时对象匿名对象是用类型名加上参数列表直接创建的对象没有名称。它的生命周期只在当前表达式或语句结束时立即析构。A(); // 调用默认构造函数创建匿名对象 A(10); // 调用带参数的构造用途当只需要临时使用一个对象时避免命名。例如传递参数给函数Solution().Sum_Solution(10); // 创建匿名 Solution 对象并调用成员函数调用后立即销毁或者配合容器、算法等如sort中的比较函数对象sort(vec.begin(), vec.end(), greaterint()); // greaterint() 是匿名对象9. 对象拷贝时的编译器优化Copy Elision / RVO / NRVO现代 C 编译器会尽量省略不必要的拷贝以提高效率。这体现在传参和返回值的优化上。情形一传值传参用已存在的对象传参会调用拷贝构造一般不会优化因为参数已经存在。用临时对象传参如f1(A(1))编译器会将构造临时对象和拷贝构造形参合并为直接在形参位置构造从而省去一次拷贝。用字面量隐式转换f1(1)也是构造拷贝优化为直接构造。情形二返回值优化RVO当函数返回一个局部对象时编译器会直接在该局部对象的内存位置上构造返回值而不是先构造局部对象再拷贝到返回值临时对象。更激进的情况下如 MSVC 2022甚至可以将局部对象构造与外部接收对象的构造合并直接在外层对象的存储空间上构造省去多次拷贝。示例A f2() { A aa; return aa; // 可能优化为直接构造返回值NRVO } A aa2 f2(); // 可能只调用一次构造aa2 直接在函数内构造为了查看优化效果可以使用g -fno-elide-constructors关闭优化但实际项目中应信任编译器优化。10. 补充知识C11 后的扩展虽然本代码主要基于 C98/03 特性但现代 C 还增加了许多重要内容10.1 移动构造函数与移动赋值为了解决深拷贝带来的性能开销C11 引入了移动语义。移动构造和移动赋值通过窃取资源而非复制适用于临时对象右值。class Stack { public: Stack(Stack s) noexcept : _a(s._a), _capacity(s._capacity), _top(s._top) { s._a nullptr; s._capacity s._top 0; } Stack operator(Stack s) noexcept { /* 类似 */ } };10.2 default 和 deletedefault显式要求编译器生成默认的拷贝构造、赋值、析构等。delete禁止拷贝或赋值如Stack(const Stack) delete;。10.3 委托构造函数一个构造函数可以调用同一类的另一个构造函数减少重复代码。Date(int year, int month, int day) : _year(year), _month(month), _day(day) {} Date() : Date(2026, 7, 23) {} // 委托给上面的构造函数总结通过这份详尽的代码解析我们系统地回顾了 C 类和对象的各个重要方面构造函数缺省参数、初始化列表、explicit拷贝控制构造、赋值、析构运算符重载成员和友元static 成员、友元、内部类匿名对象和编译器优化同时我们补充了现代 C 的移动语义、default/delete 等新特性使知识体系更加完整。理解这些细节是编写高质量 C 代码的基础也是应对面试和实际项目开发的必备技能。希望本篇博客能为你提供一份有价值的参考。

相关新闻

“采样步数=质量”是最大误区!基于127组消融实验揭示最优步数-质量-时延黄金三角

“采样步数=质量”是最大误区!基于127组消融实验揭示最优步数-质量-时延黄金三角

更多请点击: https://codechina.net 第一章:扩散模型采样步数的本质认知与误区辨析 采样步数(sampling steps)并非单纯控制生成速度的“超参开关”,而是直接决定反向扩散轨迹在隐空间中离散化逼近连续SDE/ODE解的精度…

2026/7/31 18:21:15 阅读更多 →
全栈技术助力城市生命线安全防线构筑

全栈技术助力城市生命线安全防线构筑

城市基础设施的安全运行,直接关系到千万市民的生命财产安全和城市的正常运转。随着城镇化进程持续深入,地下管网老化、极端天气频发、各类风险交织叠加等挑战日益凸显,传统的被动式、碎片化管理模式已难以应对复杂多变的安全形势。在这一背景…

2026/7/31 18:21:15 阅读更多 →
从“打卡式管理”到“意图感知办公”:AI混合办公管理的范式跃迁(含2023-2024全球TOP10案例拆解)

从“打卡式管理”到“意图感知办公”:AI混合办公管理的范式跃迁(含2023-2024全球TOP10案例拆解)

更多请点击: https://kaifayun.com 第一章:从“打卡式管理”到“意图感知办公”:AI混合办公管理的范式跃迁 传统考勤系统依赖固定时间点的签到/签退动作,将员工行为简化为二进制状态(在线/离线、在岗/缺勤&#xff09…

2026/7/31 18:20:14 阅读更多 →

最新新闻

我与 IT 这三十年:2015,大数据平台的重与轻

我与 IT 这三十年:2015,大数据平台的重与轻

2015 年,互联网公司已经很难离开数据平台。 前一年写微博头条时,说过推荐系统不是一个算法点,而是一整套能力。到了 2015 年,我对这句话的感受更深。因为越是想快速迭代业务,越会发现数据平台既是发动机,也…

2026/7/31 18:50:23 阅读更多 →
从立体声到空间音频:Ambisonics与HRTF技术实战指南

从立体声到空间音频:Ambisonics与HRTF技术实战指南

如果你点开这篇文章,大概率是想找 (G)I-DLE 的《Gimme Dat Love》表演视频,或者被“10D立体环绕”这个音效标签吸引了过来。但你可能也会疑惑:为什么一个看似娱乐向的视频内容,会出现在 CSDN 这样的技术社区?这篇文章真…

2026/7/31 18:50:23 阅读更多 →
如何在10分钟内完成原本需要3小时的AI视频生成:自动化视频制作终极指南

如何在10分钟内完成原本需要3小时的AI视频生成:自动化视频制作终极指南

如何在10分钟内完成原本需要3小时的AI视频生成:自动化视频制作终极指南 【免费下载链接】auto-video-generateor 自动视频生成器,给定主题,自动生成解说视频。用户输入主题文字,系统调用大语言模型生成故事或解说的文字&#xff0…

2026/7/31 18:50:23 阅读更多 →
告别黄色感叹号!Windows安卓驱动一键安装终极方案

告别黄色感叹号!Windows安卓驱动一键安装终极方案

告别黄色感叹号!Windows安卓驱动一键安装终极方案 【免费下载链接】Latest-adb-fastboot-installer-for-windows A Simple Android Driver installer tool for windows (Always installs the latest version) 项目地址: https://gitcode.com/gh_mirrors/la/Latest…

2026/7/31 18:50:23 阅读更多 →
电动汽车与可再生能源协同调度的鲁棒优化实践

电动汽车与可再生能源协同调度的鲁棒优化实践

1. 项目背景与核心价值去年帮某新能源车企做充电桩调度优化时,我深刻体会到电动汽车规模化接入对电网的冲击。当30辆大巴同时快充时,变电站的负载曲线就像过山车——这正是我读到这个硕士论文选题时眼前一亮的原因。可再生能源发电与电动汽车的协同调度&…

2026/7/31 18:50:23 阅读更多 →
BilibiliDown音频提取终极指南:如何从B站视频轻松提取高质量音乐

BilibiliDown音频提取终极指南:如何从B站视频轻松提取高质量音乐

BilibiliDown音频提取终极指南:如何从B站视频轻松提取高质量音乐 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh…

2026/7/31 18:49:23 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻