C++ 三之法则、五之法则和零之法则 C 三之法则、五之法则和零之法则如果你刚开始接触 C可能会被“三之法则”、“五之法则”和“零之法则”这些术语搞得一头雾水。这些其实是 C 中关于资源管理比如内存、文件句柄、网络连接等的核心设计原则。简单来说它们告诉我们当类需要管理资源时哪些特殊成员函数必须被正确定义以避免内存泄漏、重复释放等问题。本文会用通俗的语言和代码示例带你彻底搞懂这些法则。## 为什么需要这些法则C 允许你自定义类而类的对象可以通过拷贝、赋值、销毁等方式被操作。如果你没有手动管理资源C 会为你生成默认的拷贝构造函数、赋值运算符和析构函数。这些默认行为只是简单地“浅拷贝”——直接复制成员变量的值。但对于动态分配的内存比如new出来的指针浅拷贝会导致多个对象共享同一块内存一旦其中一个对象被销毁其他对象的指针就成了“悬空指针”程序就会崩溃。三之法则、五之法则和零之法则就是为了解决这个问题而生的。它们指导我们什么时候需要自定义这些特殊函数以及如何正确地实现它们。## 三之法则Rule of Three三之法则适用于 C98/03 时代。它指出如果一个类需要显式定义以下三个函数中的任何一个那么它通常需要显式定义所有三个1.析构函数~ClassName()释放资源。2.拷贝构造函数ClassName(const ClassName other)通过已有对象创建新对象。3.拷贝赋值运算符ClassName operator(const ClassName other)将已有对象赋值给另一个已存在的对象。为什么因为如果你需要手动释放资源比如delete指针说明你的类管理着资源。默认的拷贝构造函数和赋值运算符只会浅拷贝指针导致两个对象指向同一块内存。当一个对象被销毁时另一个对象的指针就无效了。所以你需要自己实现深拷贝。### 代码示例 1违反三之法则的后果cpp#include iostream#include cstringclass StringBad {private: char* data; // 动态分配的字符数组public: // 构造函数分配内存并复制字符串 StringBad(const char* str ) { data new char[strlen(str) 1]; strcpy(data, str); std::cout 构造: data \n; } // 析构函数释放内存 ~StringBad() { std::cout 析构: data \n; delete[] data; } // 注意没有定义拷贝构造函数和拷贝赋值运算符 void print() const { std::cout data \n; }};int main() { StringBad s1(Hello); StringBad s2(s1); // 使用默认拷贝构造函数浅拷贝 s2.print(); // 输出 Hello但 s2.data 和 s1.data 指向同一地址 // 程序结束时s1 和 s2 都会调用析构函数导致同一块内存被释放两次 return 0; // 输出析构: Hello (s2 先析构释放内存) // 析构: Hello (s1 析构但内存已被释放崩溃或未定义行为)}运行结果程序可能会崩溃或输出乱码因为s2被销毁后s1的data指针变成了悬空指针。### 代码示例 2正确实现三之法则cpp#include iostream#include cstringclass StringGood {private: char* data;public: // 构造函数 StringGood(const char* str ) { data new char[strlen(str) 1]; strcpy(data, str); std::cout 构造: data \n; } // 析构函数 ~StringGood() { std::cout 析构: data \n; delete[] data; } // 拷贝构造函数深拷贝 StringGood(const StringGood other) { data new char[strlen(other.data) 1]; strcpy(data, other.data); std::cout 拷贝构造: data \n; } // 拷贝赋值运算符深拷贝并处理自赋值 StringGood operator(const StringGood other) { if (this other) return *this; // 自赋值检查 delete[] data; // 释放原有资源 data new char[strlen(other.data) 1]; strcpy(data, other.data); std::cout 拷贝赋值: data \n; return *this; } void print() const { std::cout data \n; }};int main() { StringGood s1(Hello); StringGood s2(s1); // 调用拷贝构造函数 s2.print(); StringGood s3(World); s3 s1; // 调用拷贝赋值运算符 s3.print(); return 0; // 所有析构函数正常释放各自的内存}输出构造: Hello拷贝构造: HelloHello构造: World拷贝赋值: HelloHello析构: Hello析构: Hello析构: Hello每个对象都有自己的独立内存安全可靠。## 五之法则Rule of FiveC11 引入了移动语义std::move和右值引用允许我们“偷”资源而非拷贝。因此三之法则扩展成了五之法则增加了两个函数4.移动构造函数ClassName(ClassName other)将资源从右值对象“转移”到新对象。5.移动赋值运算符ClassName operator(ClassName other)将资源从右值对象转移到已存在的对象。移动语义可以显著提高性能对于临时对象我们不需要深拷贝只需将指针所有权转移然后清空源对象。### 代码示例 3五之法则的完整实现cpp#include iostream#include cstring#include utility // for std::moveclass StringMove {private: char* data;public: // 构造函数 StringMove(const char* str ) { data new char[strlen(str) 1]; strcpy(data, str); std::cout 构造: data \n; } // 析构函数 ~StringMove() { if (data) { std::cout 析构: data \n; delete[] data; } } // 拷贝构造函数 StringMove(const StringMove other) { data new char[strlen(other.data) 1]; strcpy(data, other.data); std::cout 拷贝构造: data \n; } // 拷贝赋值运算符 StringMove operator(const StringMove other) { if (this other) return *this; delete[] data; data new char[strlen(other.data) 1]; strcpy(data, other.data); std::cout 拷贝赋值: data \n; return *this; } // 移动构造函数转移资源并置空源对象 StringMove(StringMove other) noexcept : data(other.data) { other.data nullptr; // 防止源对象析构时释放资源 std::cout 移动构造\n; } // 移动赋值运算符释放自身资源转移对方资源 StringMove operator(StringMove other) noexcept { if (this other) return *this; delete[] data; // 释放当前资源 data other.data; // 转移指针 other.data nullptr; // 置空源对象 std::cout 移动赋值\n; return *this; } void print() const { if (data) std::cout data \n; else std::cout (空)\n; }};int main() { StringMove s1(Hello); StringMove s2(std::move(s1)); // 移动构造 s2.print(); // 输出 Hello s1.print(); // 输出 (空)资源已被转移 StringMove s3(World); s3 std::move(s2); // 移动赋值 s3.print(); // 输出 Hello s2.print(); // 输出 (空) return 0;}输出构造: Hello移动构造Hello(空)构造: World移动赋值Hello(空)析构: World析构: Hello析构: (空) // s1 和 s2 析构时 data 为 nullptr安全移动操作避免了深拷贝仅转移指针效率更高。## 零之法则Rule of Zero既然手动管理资源这么麻烦有没有更简单的方法有零之法则主张尽量使用 RAII资源获取即初始化类来管理资源让你的类不需要定义任何特殊成员函数。换句话说让编译器生成的默认函数就能正常工作。例如用std::string代替char*用std::vector代替动态数组用std::unique_ptr或std::shared_ptr代替裸指针。这些标准库组件已经正确实现了资源管理包括移动语义你的类只需组合它们即可。### 代码示例 4零之法则的应用cpp#include iostream#include string // 使用 std::string 自动管理字符串内存#include vector // 使用 std::vector 自动管理数组内存class Student {private: std::string name; // RAII 类自动管理内存 std::vectorint scores; // RAII 类自动管理内存public: // 构造函数只需初始化成员无需手动 new/delete Student(const std::string n, std::initializer_listint s) : name(n), scores(s) {} void print() const { std::cout name : ; for (int score : scores) std::cout score ; std::cout \n; } // 无需定义析构函数、拷贝/移动构造函数、赋值运算符 // 编译器生成的默认版本会调用 std::string 和 std::vector 的对应函数};int main() { Student s1(Alice, {90, 85, 92}); Student s2(s1); // 默认拷贝构造自动深拷贝 s2.print(); Student s3(Bob, {70, 80}); s3 std::move(s1); // 默认移动赋值自动转移资源 s3.print(); return 0;}输出Alice: 90 85 92 Bob: 90 85 92零之法则让代码更简洁、更安全推荐优先使用。## 总结-三之法则如果类需要自定义析构函数通常也需要自定义拷贝构造函数和拷贝赋值运算符C98/03 时代。-五之法则在 C11 及以后增加移动构造函数和移动赋值运算符以支持移动语义提升性能。-零之法则优先使用 RAII 类如std::string、std::vector、智能指针组合成你的类避免手动管理资源让编译器生成的默认函数正常工作。在实际开发中零之法则是最佳实践尽量让资源管理交给标准库。只有当你的类封装了非标准资源如操作系统句柄、自定义内存池时才需要考虑五之法则或三之法则。记住这些法则能让你写出更健壮、更高效的 C 代码。

相关新闻

最新新闻

聊聊前序、中序、后序表达式

聊聊前序、中序、后序表达式

聊聊前序、中序、后序表达式 在计算机科学和数学中,表达式的表示方式直接影响计算过程的复杂度和实现方式。我们日常使用的数学表达式,如 3 5 * 2,被称为中序表达式(infix notation),因为运算符位于操作数…

2026/7/25 19:25:25
间,最大化 CPU 利用率。 异步编程通过事件循环实现任务调度:当一个任务因 IO 操作需要等待时,事件循环会暂停该任务,切换到其他 ...

间,最大化 CPU 利用率。 异步编程通过事件循环实现任务调度:当一个任务因 IO 操作需要等待时,事件循环会暂停该任务,切换到其他 ...

异步编程:最大化 CPU 利用率的秘密武器 引言:为什么我们需要异步编程?想象一下,你正在一家繁忙的咖啡店排队点单。店员在等咖啡机煮咖啡时,如果只是呆呆站着等,那么后面的顾客就要等更久。但如果店员在等待…

2026/7/25 19:25:25
CVE-2022-25845 FastJSON(com.alibaba:fastjson)

CVE-2022-25845 FastJSON(com.alibaba:fastjson)

springboot 升级以及漏洞处理 CVE-2022-25845 FastJSON(com.alibaba:fastjson)包引发的漏洞fastJson-1.2.68.jar com.alibaba.fastjson.parser.ParserConfig com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer FastJSON 默认关闭 auto…

2026/7/25 19:25:25
3步安装KK-HF Patch:解锁Koikatu/Koikatsu Party完整游戏体验的终极指南

3步安装KK-HF Patch:解锁Koikatu/Koikatsu Party完整游戏体验的终极指南

3步安装KK-HF Patch:解锁Koikatu/Koikatsu Party完整游戏体验的终极指南 【免费下载链接】KK-HF_Patch Automatically translate, uncensor and update Koikatu! and Koikatsu Party! 项目地址: https://gitcode.com/gh_mirrors/kk/KK-HF_Patch 还在为Koikat…

2026/7/25 19:25:25
Windows系统优化神器:Win11Debloat一键清理你的电脑

Windows系统优化神器:Win11Debloat一键清理你的电脑

Windows系统优化神器:Win11Debloat一键清理你的电脑 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and cust…

2026/7/25 19:25:25
PPT留白不是留着空白,是建立视觉秩序

PPT留白不是留着空白,是建立视觉秩序

在进行PPT汇报的时候,经常会出现这样的状况。把所有想到的内容都一股脑地往一页里面塞,要点不停地添加,图也是越贴越多,还用很小的字体去挤空间。最后的结果就是一页全是密密麻麻的内容,听众都不知道应该先看哪里。然后…

2026/7/25 19:20:24

月新闻