C++ 三法则(Rule of Three)
定义
当一个类显式定义了以下三个特殊成员函数中的任意一个时,通常需要同时定义另外两个:
析构函数 ~T()拷贝构造函数 T(const T&)拷贝赋值运算符 T& operator=(const T&)
核心思想
如果需要自定义析构函数来释放资源(如动态内存),那么默认的拷贝构造函数和拷贝赋值运算符通常也会有问题,因为它们只会进行浅拷贝。
典型问题示例
class Bad {
public:
Bad(const char* s) : p(new std::string(s)) {}
~Bad() { delete p; } // 显式定义析构函数
private:
std::string* p;
};
int main() {
Bad a("hello");
Bad b = a; // 使用默认拷贝构造函数,进行浅拷贝
} // a.p 和 b.p 指向同一块内存,析构时重复删除同一地址,导致未定义行为
解决方案
手动定义拷贝构造函数和拷贝赋值运算符来实现深拷贝:
class Good {
public:
Good(const char* s) : p(new std::string(s)) {}
// 析构函数
~Good() { delete p; }
// 拷贝构造函数
Good(const Good& other) : p(new std::string(*other.p)) {}
// 拷贝赋值运算符
Good& operator=(const Good& other) {
if (this != &other) {
delete p;
p = new std::string(*other.p);
}
return *this;
}
private:
std::string* p;
};
C++ 五法则(Rule of Five)
定义
在C++11及以后,当一个类需要自定义析构函数时,通常需要同时定义以下五个特殊成员函数:
析构函数 ~T()拷贝构造函数 T(const T&)拷贝赋值运算符 T& operator=(const T&)移动构造函数 T(T&&) noexcept移动赋值运算符 T& operator=(T&&) noexcept
核心思想
C++11引入了右值引用和移动语义,当类需要管理资源时,除了传统的拷贝操作外,还需要考虑移动操作以提高性能。如果需要自定义拷贝构造函数或拷贝赋值运算符,通常也需要自定义移动构造函数和移动赋值运算符。
完整示例
class Resource {
public:
// 构造函数
Resource(const char* s) : p(new std::string(s)) {}
// 析构函数
~Resource() { delete p; }
// 拷贝构造函数
Resource(const Resource& other) : p(new std::string(*other.p)) {}
// 拷贝赋值运算符
Resource& operator=(const Resource& other) {
if (this != &other) {
delete p;
p = new std::string(*other.p);
}
return *this;
}
// 移动构造函数
Resource(Resource&& other) noexcept : p(other.p) {
other.p = nullptr;
}
// 移动赋值运算符
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete p;
p = other.p;
other.p = nullptr;
}
return *this;
}
private:
std::string* p;
};
移动语义的优势
移动操作可以避免不必要的深拷贝,直接转移资源所有权,提高性能:
Resource createResource() {
return Resource("example"); // 返回临时对象,触发移动构造
}
int main() {
Resource a("hello");
Resource b = std::move(a); // 显式移动,a的资源转移给b
}
现代C++建议
遵循五法则的同时,更推荐使用"零法则"(Rule of Zero):通过使用智能指针等RAII类型来自动管理资源,让编译器生成默认的特殊成员函数。


1239

被折叠的 条评论
为什么被折叠?



