编译器默认生成的c++类六大成员函数

编译器默认生成的六大成员函数

当你定义一个空类时,例如:

class Empty {};

如果代码中没有显式定义任何成员函数,C++编译器会在需要时(例如,代码中实际调用了这些函数)为你自动生成以下六个特殊成员函数(Special Member Functions):

  1. 默认构造函数 (Default Constructor): Empty();

    • 用于创建对象,当没有提供任何初始化参数时被调用。
    • 例如:Empty e1;
  2. 析构函数 (Destructor): ~Empty();

    • 用于对象销毁,在对象生命周期结束时被调用。
    • 它负责清理资源,虽然对于空类来说没什么可清理的。
  3. 拷贝构造函数 (Copy Constructor): Empty(const Empty& other);

    • 用于从一个同类对象创建新对象。
    • 例如:Empty e2(e1); 或 Empty e3 = e1;
  4. 拷贝赋值运算符 (Copy Assignment Operator): Empty& operator=(const Empty& other);

    • 用于将一个已存在的同类对象的值赋给另一个已存在的对象。
    • 例如:e2 = e1;
  5. 移动构造函数 (Move Constructor) (C++11及以后): Empty(Empty&& other) noexcept;

    • 用于从一个右值(通常是临时对象)“窃取”其资源来创建新对象,避免不必要的拷贝。
    • 例如:Empty e4(std::move(e1));
  6. 移动赋值运算符 (Move Assignment Operator) (C++11及以后): Empty&operator=(Empty&& other) noexcept;

    • 用于将一个右值对象的资源“窃取”并赋给一个已存在的对象。
    • 例如:e3 = std::move(e2);
#include <iostream>
#include <utility>class Empty {};int main() {std::cout << "创建一个默认对象 e1..." << std::endl;Empty e1;std::cout << "使用拷贝构造函数创建 e2..." << std::endl;Empty e2(e1);std::cout << "使用拷贝赋值运算符..." << std::endl;Empty e3;e3 = e2;std::cout << "使用移动构造函数创建 e4..." << std::endl;Empty e4(std::move(e1));std::cout << "使用移动赋值运算符..." << std::endl;e3 = std::move(e2);std::cout << "程序结束,对象将被销毁。" << std::endl;return0;
}

这段代码可以成功编译和运行,这雄辩地证明了,即使我们没有为 Empty 类编写任何一个函数,编译器也已经为我们提供了所有必要的“基础设施”来完成对象的创建、拷贝、移动和销毁。

类的六大成员函数生成规则及“三/五/零法则”

了解了“有什么”之后,一个优秀的工程师还应该了解“为什么有”和“什么时候没有”。

生成规则

编译器并非无脑生成这些函数。它的行为遵循一套精密的规则:

  • 只在需要时生成:这些函数只在它们被ODR-used(One Definition
    Rule-used,可以简单理解为“被实际调用”)时,编译器才会去定义它们。

  • 用户优先:如果你显式声明了任何一个特殊成员函数(即使是用 =delete 或 =default),编译器就不会再为该函数生成默认版本。

  • 复杂的关联规则:声明一个函数会“抑制”其他函数的自动生成。这正是“三/五法则”的核心。

    • 重要:一旦你声明了自定义的析构函数、拷贝构造或拷贝赋值,编译器将不会自动生成移动构造和移动赋值函数。这是因为自定义的析-构/拷贝行为可能与默认的移动行为不兼容。
    • 反之,声明了移动操作,也会影响拷贝操作的自动生成。
    • 三法则 (Rule of Three): (C++03) 如果你显式声明了析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,通常意味着你需要同时管理这三者,因为类内可能含有需要深度拷贝的资源(如裸指针)。此时,编译器将不再自动生成它认为你可能需要自己实现的拷贝操作。
    • 五法则 (Rule of Five): (C++11) 这是“三法则”的扩展。如果你声明了上述三者中的任何一个,或者声明了移动构造函数或移动赋值运算符,编译器将认为你对资源管理有特殊意图。

现代C++的智慧:“零法则” (Rule of Zero)

三/五法则”是C++开发者必须掌握的知识,但它们也反映了一种更底层的设计问题:手动资源管理。现代C++推崇**“零法则” (Rule of Zero)**。
核心思想:设计你的类,使其不需要编写任何自定义的析构、拷贝/移动构造和赋值函数。将所有资源的所有权交给专门的资源管理类(如 std::string, std::vector, std::unique_ptr, std::shared_ptr)。

当你遵循“零法则”时,你的类(即使非空)也能像空类一样,让编译器为其生成正确、高效的特殊成员函数。这些标准库组件本身已经完美地实现了“五法则”,你的类只需组合它们,就能自动获得正确的资源管理行为。这使得代码更简洁、更安全、更易于维护。

一个有趣的延伸:sizeof(Empty) 等于多少?

与空类相关的另一个高频面试题是:sizeof(Empty) 的结果是多少?
答案不是0,而是1。
为什么是1?
C++标准规定,任何两个不同的对象在内存中都必须有不同的地址。如果一个空类的大小为0,那么当你创建一个该类的数组时:

Empty arr[10];

&arr[0] 和 &arr[1] 的地址将会相同,这将导致指针算术和数组索引彻底失效。为了保证对象标识(identity)的唯一性,编译器会为空类“填充”一个字节。这个字节不存储任何有用的数据,仅仅是为了“占位”。

空基类优化 (Empty Base Optimization, EBO)

class BaseEmpty {}; // sizeof(BaseEmpty) == 1class Derived : public BaseEmpty {int x; // sizeof(int) == 4
};// sizeof(Derived) 通常是 4,而不是 1 + 4 = 5

在这种情况下,编译器可以将空基类的“1字节”与派生类的成员(如 x)的数据存储在相同的地址,或者说,让空基类不占用任何额外的空间。这样既满足了“不同对象地址不同”的原则,又避免了内存浪费。这是C++零开销原则 Zero-overhead principle的一个体现。

参考文献:

面试官问“空类里有什么”?别再答“什么都没有”了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918515.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918515.shtml
英文地址,请注明出处:http://en.pswp.cn/news/918515.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能概念:常见的大模型微调方法

文章目录一、微调技术的底层逻辑1.1 预训练与微调的关系1.2 核心目标&#xff1a;适配任务与数据二、经典微调方法详解2.1 全量微调&#xff08;Full Fine-Tuning&#xff09;2.2 冻结层微调&#xff08;Layer-Freezing Fine-Tuning&#xff09;2.3 参数高效微调&#xff08;Pa…

动态路由协议(一)

1. 动态路由 概述 静态路由在大网络里太麻烦&#xff08;设备多、配置量大&#xff0c;拓扑变了还要手动改&#xff09; 静态路由是由工程师手动配置和维护的路由条目&#xff0c;命令行简单明确&#xff0c;适用于小型或稳定的网络。静态路由有以下问题&#xff1a; 无法适…

LINUX812 shell脚本:if else,for 判断素数,创建用户

问题 [rootweb ~]# for((i2;i<n;i)) > if [ $n -ne $i ] && [ $((n%i)) -eq 0 ];then -bash: 未预期的符号 if 附近有语法错误 您在 /var/spool/mail/root 中有邮件 [rootweb ~]#[rootweb ~]# cat judgeprimeok.sh declare -i n read -p "please type the n…

游戏中角色持枪:玩家操控角色,角色转向时枪也要转向

角色持有枪&#xff0c;玩家&#xff08;你&#xff09;操控角色&#xff0c;那么&#xff0c;在角色转向时&#xff0c;枪也要转向。 先看看简单情况&#xff1a;假定角色只面向左或右方向&#xff0c;pygame中用这句来实现&#xff1a;pos self.facing * self.gun_offset s…

深度学习入门Day8:生成模型革命——从GAN到扩散模型

一、开篇&#xff1a;创造力的算法革命从昨天的Transformer到今天的生成模型&#xff0c;我们正从"理解"世界迈向"创造"世界。生成对抗网络(GAN)和扩散模型(Diffusion Model)代表了当前生成式AI的两大主流范式&#xff0c;它们让机器能够生成逼真的图像、音…

基于WRF-Chem的不同气溶胶的辐射效应的研究

前言目前我对于气溶胶辐射效应的理解就是设计敏感性实验&#xff0c;基础实验打开气溶胶参与辐射开关&#xff08;aer_ra_feedback&#xff09;&#xff0c;其他的实验则关闭气溶胶参与辐射过程开关&#xff0c;也有去掉某些气溶胶的影响&#xff0c;如黑碳&#xff08;BC&…

专题:2025人形机器人与服务机器人技术及市场报告|附130+份报告PDF汇总下载

原文链接&#xff1a;https://tecdat.cn/?p43583 当特斯拉Optimus在工厂里精准分拣电池&#xff0c;当普渡机器人在酒店完成跨楼层配送&#xff0c;一个万亿级的智能革命正在拉开序幕。服务机器人与人形机器人不再是实验室里的概念&#xff0c;而是正在重塑制造业、服务业的“…

JS 模块化与打包工具

一、模块化体系&#xff1a;ESM vs CJS 深入1.语法与静态性(1)ESM:静态语法&#xff0c;可被打包器做 Tree-shakingexport function play() {}export default ...import { play } from ./mod.js(2)CJS:运行时 require() , 分析能力弱&#xff0c;不利于 Tree-shaking2.Node 解析…

防御保护11

带宽管理 --- 设备对自身的流量进行管理和控制&#xff0c;去提供带宽保证、带宽限制等等功能。 带宽限制 带宽保证 连接数限制 应用场景 实现带宽管理 带宽通道 --- 定义了被管理对象所能使用的带宽资源 整体的保证带宽和最大带宽&#xff1b; SW1-SW2&#xff1a;VLAN 201 --…

[激光原理与应用-254]:理论 - 几何光学 - 自动对焦的原理

自动对焦&#xff08;Auto Focus, AF&#xff09;是现代光学系统&#xff08;如相机、手机摄像头、监控设备等&#xff09;的核心功能之一&#xff0c;其原理是通过检测成像面的清晰度或测量物体距离&#xff0c;驱动透镜组移动至最佳对焦位置。以下是自动对焦的详细原理及技术…

【Python办公】Mermaid代码转图片工具 - Tkinter GUI版本

目录 专栏导读 项目简介 功能特性 🎨 直观的图形界面 📝 代码编辑功能 🖼️ 图片生成与预览 💾 文件操作 ⚡ 性能优化 技术架构 核心技术栈 架构设计 安装与使用 环境要求 依赖安装 运行程序 使用步骤 代码示例 基本流程图 时序图 甘特图 核心代码解析 1. 主类结构 2. …

【Activiti】要点初探

Activiti 7.0.0配置 流程配置节点流程XML流程部署部署后会操作表&#xff1a;&#xff08;每部署一次增加一条记录&#xff09; ACT_RE_DEPLOYMENT 流程定义部署表 ACT_RE_PROCDEF 流程定义表 ACT_GE_BYTEARRAY 流程启动查看任务&#xff08;张三要查看准备办理任务&#xff0…

VBS 字符串处理

一. 字符串是由Unicode字符组成的一串字符。通常由数字&#xff0c;字母&#xff0c;符号组成。二. 常用函数1. 消除空格 Ltrim: 删除字符串左侧的空格。 Rtrim: 删除字符串右侧的空格。 trim: 删除字符串左侧和右侧的空格。a" hello " b"sx"msgbo…

《算法导论》第 21 章-用于不相交集合的数据结构

引言不相交集合&#xff08;Disjoint Set&#xff09;&#xff0c;也称为并查集&#xff08;Union-Find&#xff09;&#xff0c;是一种非常实用的数据结构&#xff0c;主要用于处理一些元素分组的问题。它支持高效的集合合并和元素查找操作&#xff0c;在很多算法中都有重要应…

基于51单片机RFID智能门禁系统红外人流量计数统计

1 系统功能介绍 本设计基于STC89C52单片机&#xff0c;集成RFID读卡器、红外避障传感器、继电器、LCD1602液晶显示和蜂鸣器&#xff0c;实现智能门禁与人流量统计功能。系统能够识别合法的RFID卡开门&#xff0c;并实时统计通过人数&#xff0c;具有安全报警和直观显示功能。具…

c#,vb.net全局多线程锁,可以在任意模块或类中使用,但尽量用多个锁提高效率

Public ReadOnly LockObj As New Object() 全局多线程锁 VB.NET模块中的LockObj 可以在任意模块或类中使用吧 在 VB.NET 中&#xff0c;模块&#xff08;Module&#xff09;中声明的 Public ReadOnly LockObj 可以被其他模块或类访问和使用&#xff0c;但需要注意其可见性范围…

企业安全运维服务计划书

安全运维服务计划书 一、概述 为保障企业信息系统安全、稳定、高效运行,防范各类网络安全风险,提升整体安全防护能力,特制定本安全运维服务计划书。本计划旨在通过系统化、规范化的安全运维流程,全面识别、评估、处置并持续监控企业网络环境中的安全风险,构建主动防御与…

小杰python高级(four day)——matplotlib库

1.绘制子图的方式pyplot中函数subplotFigure类中的函数add_subplotpyplot中函数subplotsfig, ax plt.subplots(nrows1, ncols1, *, sharexFalse, shareyFalse,squeezeTrue, subplot_kwNone, gridspec_kwNone, **fig_kw) 功能&#xff1a;绘制多个子图&#xff0c;可以一次生成…

C# 编程out 参数需要在函数体内部初始化,然后引用的时候无需初始化

核心规则方法内部必须初始化&#xff1a;在方法体中&#xff0c;必须在方法返回前对 out 参数显式赋值&#xff08;未赋值会导致编译错误&#xff09;调用时无需初始化&#xff1a;调用方传递 out 参数前不需要初始化变量&#xff08;可直接使用未赋值的局部变量&#xff09;下…

【Redis在数据治理与数据隐私保护策略中的优化】

## Redis的自动补全功能&#xff1a;用户体验的无缝之助Redis作为一款高效的开源缓存数据库&#xff0c;始终在用户体验优化方面走在前列。其自动补全功能的引入&#xff0c;为用户带来了全新的搜索体验。这种功能不仅提升了搜索效率&#xff0c;更为用户提供了更智能化的服务。…