题目
编写程序,阐述现代 C++(C++11 之后)取代经典三法则的至高准则——“现代五法则”(Rule of Five):如果一个类需要自定义析构函数,那么它必须同时显式定义或明确处理以下五个特殊成员函数:①析构函数、②拷贝构造函数、③拷贝赋值运算符、④移动构造函数、⑤移动赋值运算符。手写一个完备的高性能自定义动态字符串类 MyString,集成五大金刚并验证其在不同操作场景下的精准分流。
输入格式:无需外部输入。
输出格式:输出五法则下各类构造、赋值、深拷贝与移动转移的控制台全生命周期追踪。
数据范围与约定:C++ 现代五法则(Rule of Five)、特殊成员函数合成抑制机制。
思路与算法
1. 为什么从三法则升级到五法则
C++11 引入移动语义后,若仅实现拷贝三件套,当面对临时对象或 std::move 时,编译器只能退化为慢速深拷贝,无法享受到移动语义带来的性能飞跃。
2. 移动与拷贝的抑制关系
一旦类中显式声明了自定义析构函数或拷贝函数,编译器将绝对不会自动合成默认的移动构造函数与移动赋值运算符!若需要移动必须手工编写。
3. 五法则缺一不可
遵循现代五法则,能够让自定义类型与 STL 容器(如 vector 扩容与排序)完美结合,达到与原生标准类型等同的极致效率。
完整程序
#include <iostream>
#include <cstring>
#include <utility>
class MyString {
private:
char* data_;
size_t len_;
public:
// 基础构造
MyString(const char* str = "") {
len_ = std::strlen(str);
data_ = new char[len_ + 1];
std::strcpy(data_, str);
std::cout << " (构造) 创建: \"" << data_ << "\"\n";
}
// 1. 析构函数
~MyString() {
std::cout << " (析构) 销毁: \"" << (data_ ? data_ : "null") << "\"\n";
delete[] data_;
}
// 2. 拷贝构造函数 (深拷贝)
MyString(const MyString& other) {
len_ = other.len_;
data_ = new char[len_ + 1];
std::strcpy(data_, other.data_);
std::cout << " (深拷贝构造) 复制: \"" << data_ << "\"\n";
}
// 3. 拷贝赋值运算符 (深拷贝赋值)
MyString& operator=(const MyString& other) {
if (this != &other) {
char* newData = new char[other.len_ + 1];
std::strcpy(newData, other.data_);
delete[] data_;
data_ = newData;
len_ = other.len_;
}
std::cout << " (深拷贝赋值) 覆盖为: \"" << data_ << "\"\n";
return *this;
}
// 4. 移动构造函数 (偷取指针)
MyString(MyString&& other) noexcept : data_(other.data_), len_(other.len_) {
other.data_ = nullptr;
other.len_ = 0;
std::cout << " (移动构造) 偷取资源: \"" << (data_ ? data_ : "null") << "\"\n";
}
// 5. 移动赋值运算符 (偷取转移)
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
len_ = other.len_;
other.data_ = nullptr;
other.len_ = 0;
}
std::cout << " (移动赋值) 接管资源: \"" << (data_ ? data_ : "null") << "\"\n";
return *this;
}
const char* c_str() const { return data_ ? data_ : ""; }
};
int main() {
std::cout << "--- 1. 普通构造与拷贝构造 ---\n";
MyString s1("Hello");
MyString s2 = s1; // 触发拷贝构造
std::cout << "\n--- 2. 移动构造 ---\n";
MyString s3 = std::move(s1); // 触发移动构造,s1 资源被抽干
std::cout << "\n--- 3. 拷贝赋值与移动赋值 ---\n";
s2 = s3; // 触发拷贝赋值
s2 = std::move(s3); // 触发移动赋值
std::cout << "\n--- 4. 作用域结束释放 ---\n";
return 0;
}运行示例
输出:
--- 1. 普通构造与拷贝构造 ---
(构造) 创建: "Hello"
(深拷贝构造) 复制: "Hello"
--- 2. 移动构造 ---
(移动构造) 偷取资源: "Hello"
--- 3. 拷贝赋值与移动赋值 ---
(深拷贝赋值) 覆盖为: "Hello"
(移动赋值) 接管资源: "Hello"
--- 4. 作用域结束释放 ---
(析构) 销毁: "null"
(析构) 销毁: "Hello"
(析构) 销毁: "null"