题目

编写程序,阐述现代 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"