# 三五法则 ### **Rule of Five / Rule of Zero** C++11引入了"五法则"(Rule of Five),这是对C++98"三法则"的扩展: 1. **析构函数** (Destructor) 2. **拷贝构造函数** (Copy Constructor) 3. **拷贝赋值运算符** (Copy Assignment Operator) 4. **移动构造函数** (Move Constructor) - C++11新增 5. **移动赋值运算符** (Move Assignment Operator) - C++11新增 ### **编译器生成规则** 编译器按照以下规则自动生成这些特殊成员函数: #### **自动生成条件** - **拷贝构造函数**:仅当没有用户定义的移动构造函数、移动赋值运算符、析构函数时自动生成 - **移动构造函数**:仅当没有用户定义的拷贝构造函数、拷贝赋值运算符、移动赋值运算符、析构函数时自动生成 - **拷贝赋值运算符**:仅当没有用户定义的移动赋值运算符时自动生成 - **移动赋值运算符**:仅当没有用户定义的拷贝构造函数、拷贝赋值运算符、移动构造函数、析构函数时自动生成 #### **为什么这样设计?** 1. **避免意外的浅拷贝**:如果类需要自定义移动语义,通常也需要自定义拷贝语义 2. **性能考虑**:强制程序员明确指定拷贝行为,避免意外的深拷贝 3. **资源管理安全**:确保资源管理的一致性和正确性 4. **RAII原则**:遵循资源获取即初始化原则 ## **完整示例代码** ``` #include #include #include #include // 示例1: 只定义移动构造函数的类 class MoveOnly { private: std::unique_ptr data; size_t size; public: // 构造函数 MoveOnly(size_t s) : size(s), data(std::make_unique(s)) { std::cout << "MoveOnly 构造函数, size = " << size << std::endl; for(size_t i = 0; i < size; ++i) { data[i] = static_cast(i); } } // 自定义移动构造函数 MoveOnly(MoveOnly&& other) noexcept : data(std::move(other.data)), size(other.size) { std::cout << "MoveOnly 移动构造函数" << std::endl; other.size = 0; } // 自定义移动赋值运算符 MoveOnly& operator=(MoveOnly&& other) noexcept { if(this != &other) { std::cout << "MoveOnly 移动赋值运算符" << std::endl; data = std::move(other.data); size = other.size; other.size = 0; } return *this; } // 拷贝构造函数被删除(编译器不会自动生成) // MoveOnly(const MoveOnly&) = delete; // 隐式删除 // 拷贝赋值运算符被删除 // MoveOnly& operator=(const MoveOnly&) = delete; // 隐式删除 // 析构函数 ~MoveOnly() { std::cout << "MoveOnly 析构函数, size = " << size << std::endl; } void print() const { std::cout << "MoveOnly data (size=" << size << "): "; if(data && size > 0) { for(size_t i = 0; i < std::min(size, size_t(5)); ++i) { std::cout << data[i] << " "; } if(size > 5) std::cout << "..."; } else { std::cout << "(moved/empty)"; } std::cout << std::endl; } }; // 示例2: 完整实现所有特殊成员函数的类 class CompleteClass { private: int* data; size_t size; public: // 构造函数 CompleteClass(size_t s) : size(s), data(new int[s]) { std::cout << "CompleteClass 构造函数, size = " << size << std::endl; for(size_t i = 0; i < size; ++i) { data[i] = static_cast(i * 10); } } // 拷贝构造函数 CompleteClass(const CompleteClass& other) : size(other.size), data(new int[other.size]) { std::cout << "CompleteClass 拷贝构造函数" << std::endl; for(size_t i = 0; i < size; ++i) { data[i] = other.data[i]; } } // 移动构造函数 CompleteClass(CompleteClass&& other) noexcept : data(other.data), size(other.size) { std::cout << "CompleteClass 移动构造函数" << std::endl; other.data = nullptr; other.size = 0; } // 拷贝赋值运算符 CompleteClass& operator=(const CompleteClass& other) { std::cout << "CompleteClass 拷贝赋值运算符" << std::endl; if(this != &other) { delete[] data; size = other.size; data = new int[size]; for(size_t i = 0; i < size; ++i) { data[i] = other.data[i]; } } return *this; } // 移动赋值运算符 CompleteClass& operator=(CompleteClass&& other) noexcept { std::cout << "CompleteClass 移动赋值运算符" << std::endl; if(this != &other) { delete[] data; data = other.data; size = other.size; other.data = nullptr; other.size = 0; } return *this; } // 析构函数 ~CompleteClass() { std::cout << "CompleteClass 析构函数, size = " << size << std::endl; delete[] data; } void print() const { std::cout << "CompleteClass data (size=" << size << "): "; if(data && size > 0) { for(size_t i = 0; i < std::min(size, size_t(5)); ++i) { std::cout << data[i] << " "; } if(size > 5) std::cout << "..."; } else { std::cout << "(moved/empty)"; } std::cout << std::endl; } }; // 示例3: 默认行为的类(所有特殊成员函数都由编译器生成) class DefaultClass { private: std::vector data; public: DefaultClass(size_t s) : data(s) { std::cout << "DefaultClass 构造函数, size = " << s << std::endl; for(size_t i = 0; i < s; ++i) { data[i] = static_cast(i * 100); } } // 所有特殊成员函数都使用默认实现 // DefaultClass(const DefaultClass&) = default; // 拷贝构造 // DefaultClass(DefaultClass&&) = default; // 移动构造 // DefaultClass& operator=(const DefaultClass&) = default; // 拷贝赋值 // DefaultClass& operator=(DefaultClass&&) = default; // 移动赋值 // ~DefaultClass() = default; // 析构函数 void print() const { std::cout << "DefaultClass data (size=" << data.size() << "): "; for(size_t i = 0; i < std::min(data.size(), size_t(5)); ++i) { std::cout << data[i] << " "; } if(data.size() > 5) std::cout << "..."; std::cout << std::endl; } }; // 测试函数 void testMoveOnly() { std::cout << "\n=== 测试 MoveOnly 类 ===" << std::endl; MoveOnly obj1(5); obj1.print(); // 移动构造 MoveOnly obj2 = std::move(obj1); std::cout << "移动后:" << std::endl; obj1.print(); // 应该显示 moved/empty obj2.print(); // 移动赋值 MoveOnly obj3(3); obj3.print(); obj3 = std::move(obj2); std::cout << "移动赋值后:" << std::endl; obj2.print(); // 应该显示 moved/empty obj3.print(); // 下面的代码会编译错误,因为拷贝构造函数被删除 // MoveOnly obj4 = obj3; // 编译错误! // MoveOnly obj5(obj3); // 编译错误! std::cout << "注意: 拷贝构造函数已被删除,无法进行拷贝操作" << std::endl; } void testCompleteClass() { std::cout << "\n=== 测试 CompleteClass 类 ===" << std::endl; CompleteClass obj1(4); obj1.print(); // 拷贝构造 CompleteClass obj2 = obj1; std::cout << "拷贝后:" << std::endl; obj1.print(); // 原对象不变 obj2.print(); // 移动构造 CompleteClass obj3 = std::move(obj1); std::cout << "移动后:" << std::endl; obj1.print(); // 应该显示 moved/empty obj3.print(); // 拷贝赋值 obj1 = obj2; std::cout << "拷贝赋值后:" << std::endl; obj1.print(); obj2.print(); // 移动赋值 obj2 = std::move(obj3); std::cout << "移动赋值后:" << std::endl; obj2.print(); obj3.print(); // 应该显示 moved/empty } void testDefaultClass() { std::cout << "\n=== 测试 DefaultClass 类 ===" << std::endl; DefaultClass obj1(3); obj1.print(); // 拷贝构造(使用默认实现) DefaultClass obj2 = obj1; std::cout << "拷贝后:" << std::endl; obj1.print(); obj2.print(); // 移动构造(使用默认实现) DefaultClass obj3 = std::move(obj1); std::cout << "移动后:" << std::endl; obj1.print(); // vector的移动后状态是有效但未指定的 obj3.print(); } // 编译时类型检查函数 template void checkCopyMoveability() { std::cout << "\n=== " << typeid(T).name() << " 的类型特征 ===" << std::endl; std::cout << "可拷贝构造: " << std::is_copy_constructible_v << std::endl; std::cout << "可移动构造: " << std::is_move_constructible_v << std::endl; std::cout << "可拷贝赋值: " << std::is_copy_assignable_v << std::endl; std::cout << "可移动赋值: " << std::is_move_assignable_v << std::endl; } int main() { std::cout << "C++ 移动构造函数与拷贝构造函数关系演示" << std::endl; std::cout << "========================================" << std::endl; // 类型特征检查 checkCopyMoveability(); checkCopyMoveability(); checkCopyMoveability(); // 功能测试 try { testMoveOnly(); testCompleteClass(); testDefaultClass(); } catch(const std::exception& e) { std::cerr << "异常: " << e.what() << std::endl; } std::cout << "\n程序结束" << std::endl; return 0; } ``` ## **预期输出分析** 运行程序后,你会看到: 1. **MoveOnly类**: - `可拷贝构造: 0` - 拷贝构造函数被删除 - `可移动构造: 1` - 移动构造函数可用 - 只能进行移动操作,无法拷贝 2. **CompleteClass类**: - 所有操作都可用 - 可以看到拷贝和移动的不同行为 3. **DefaultClass类**: - 使用编译器生成的默认实现 - 所有操作都可用 ## **最佳实践建议** ### **1. Rule of Zero(零法则)** 尽量使用 RAII 和标准库容器,让编译器自动生成所有特殊成员函数: ``` class GoodClass { std::vector data; // 使用标准容器 std::unique_ptr res; // 使用智能指针 // 无需自定义任何特殊成员函数 }; ``` ### **2. Rule of Five(五法则)** 如果必须自定义其中一个,通常需要自定义所有五个: ``` class ResourceManager { public: ~ResourceManager(); // 析构函数 ResourceManager(const ResourceManager&); // 拷贝构造 ResourceManager& operator=(const ResourceManager&); // 拷贝赋值 ResourceManager(ResourceManager&&) noexcept; // 移动构造 ResourceManager& operator=(ResourceManager&&) noexcept; // 移动赋值 }; ``` ### **3. 明确删除不需要的操作** ``` class NonCopyable { public: NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; NonCopyable(NonCopyable&&) = default; NonCopyable& operator=(NonCopyable&&) = default; }; ```