遵循“三/五/零法则”确保C++类正确管理资源:无需手动管理资源时遵循零法则,编译器自动生成默认函数;需管理资源时显式定义析构函数、拷贝构造、拷贝赋值、移动构造和移动赋值函数,防止内存泄漏、悬挂指针和双重释放;使用智能指针如std::unique_ptr和std::shared_ptr可自动管理资源,结合移动语义避免不必要的拷贝,提升性能。

编写遵循“三/五/零法则”的 C++ 类,关键在于明确类的资源管理职责。如果类需要管理资源(如动态分配的内存),就需要遵循“三/五法则”;如果不需要,就遵循“零法则”。
零法则(Rule of Zero):
最简单的情况。如果你的类不需要显式管理任何资源(例如,它只包含
int
double
std::string
#include <string>
class Person {
public:
Person(std::string name, int age) : name_(name), age_(age) {}
private:
std::string name_;
int age_;
}; // 编译器会自动生成析构函数、拷贝构造函数和拷贝赋值运算符三/五法则(Rule of Three/Five):
立即学习“C++免费学习笔记(深入)”;
如果你的类需要管理资源,比如动态分配内存,那么你需要显式地定义析构函数、拷贝构造函数和拷贝赋值运算符(这是“三法则”)。 C++11 引入了移动构造函数和移动赋值运算符,因此“三法则”扩展为“五法则”。
考虑一个简单的字符串类:
#include <cstring> // 包含 strlen, strcpy, delete[]
class MyString {
public:
// 构造函数
MyString(const char* str = nullptr) {
if (str) {
size_ = std::strlen(str);
data_ = new char[size_ + 1];
std::strcpy(data_, str);
} else {
size_ = 0;
data_ = new char[1];
data_[0] = '\0';
}
}
// 析构函数
~MyString() {
delete[] data_;
}
// 拷贝构造函数
MyString(const MyString& other) {
size_ = other.size_;
data_ = new char[size_ + 1];
std::strcpy(data_, other.data_);
}
// 拷贝赋值运算符
MyString& operator=(const MyString& other) {
if (this != &other) { // 防止自赋值
delete[] data_; // 释放旧资源
size_ = other.size_;
data_ = new char[size_ + 1];
std::strcpy(data_, other.data_);
}
return *this;
}
// 移动构造函数 (C++11)
MyString(MyString&& other) noexcept : size_(other.size_), data_(other.data_) {
other.size_ = 0;
other.data_ = nullptr;
}
// 移动赋值运算符 (C++11)
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = other.data_;
other.size_ = 0;
other.data_ = nullptr;
}
return *this;
}
const char* c_str() const { return data_; }
private:
size_t size_;
char* data_;
};解释:
noexcept
为什么需要遵循“三/五/零法则”?不遵循会有什么问题?
不遵循“三/五/零法则”会导致各种资源管理问题,最常见的是内存泄漏和悬挂指针。
如何使用智能指针简化资源管理,并避免手动管理内存?
智能指针(如
std::unique_ptr
std::shared_ptr
std::weak_ptr
std::unique_ptr
std::unique_ptr
std::unique_ptr
#include <memory>
class MyClass {
public:
MyClass() {
data_ = new int[10];
}
~MyClass() {
delete[] data_;
}
private:
int* data_;
};
int main() {
std::unique_ptr<MyClass> ptr(new MyClass()); // 使用 unique_ptr 管理 MyClass 对象
// 当 ptr 超出作用域时,MyClass 对象会被自动销毁,data_ 也会被释放
return 0;
}使用
std::unique_ptr
std::unique_ptr
unique_ptr
std::shared_ptr
std::shared_ptr
std::shared_ptr
shared_ptr
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // ptr1 和 ptr2 共享同一个 int 对象
// 当 ptr1 和 ptr2 都超出作用域时,int 对象才会被销毁
return 0;
}使用
std::shared_ptr
std::weak_ptr
std::weak_ptr
std::weak_ptr
std::shared_ptr
std::weak_ptr
移动语义如何提升性能?何时应该使用移动语义?
移动语义通过“窃取”另一个对象的资源来避免不必要的拷贝操作,从而提升性能。 它特别适用于以下场景:
返回大型对象: 当函数返回一个大型对象时,移动语义可以避免拷贝构造函数的调用。
MyString createString() {
MyString str("Hello, world!");
return str; // str 会被移动到函数调用者
}临时对象: 当使用临时对象时,移动语义可以避免拷贝构造函数和拷贝赋值运算符的调用。
MyString str1 = MyString("Hello");
MyString str2 = std::move(str1); // 将 str1 移动到 str2std::move
str1
容器操作: 当向容器中插入元素时,移动语义可以避免拷贝操作。例如,
std::vector::push_back
std::vector<MyString> vec;
MyString str("Hello");
vec.push_back(std::move(str)); // 将 str 移动到 vec 中总而言之,遵循“三/五/零法则”是编写健壮、高效的 C++ 代码的关键。 使用智能指针可以简化资源管理,而移动语义可以避免不必要的拷贝操作,提升性能。在设计类时,仔细考虑类的资源管理职责,并选择合适的资源管理策略。
以上就是如何编写一个遵循“三/五/零之法则”的C++类来管理内存的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号