C++默认函数生成问题详解与解决方法

C++默认函数生成问题详解与解决方法

1. 默认函数生成规则演变

1.1 C++03 vs C++11 vs C++14 vs C++17 vs C++20

// C++03: 三大件(拷贝构造、拷贝赋值、析构)默认生成
class Cpp03Class {
public:
    // 编译器自动生成:
    // 1. Cpp03Class()
    // 2. ~Cpp03Class()
    // 3. Cpp03Class(const Cpp03Class&)
    // 4. Cpp03Class& operator=(const Cpp03Class&)
};

// C++11: 增加了移动语义和=default/=delete
class Cpp11Class {
public:
    // 编译器可能自动生成:
    // 1. 默认构造函数
    // 2. 析构函数
    // 3. 拷贝构造函数
    // 4. 拷贝赋值运算符
    // 5. 移动构造函数(条件复杂)
    // 6. 移动赋值运算符(条件复杂)
    
    // 规则变化:用户声明析构函数会抑制移动操作生成
    virtual ~Cpp11Class() = default;  // 这会抑制移动操作生成
};

// C++17: 规则进一步调整
class Cpp17Class {
public:
    // 主要变化:聚合类的默认构造函数生成规则变化
};

2. 六大特殊成员函数的生成规则

2.1 默认构造函数

class DefaultConstructor {
    int x;
    std::string s;
    
public:
    // 情况1:没有任何构造函数 -> 编译器生成默认构造函数
    // DefaultConstructor() {}
    
    // 情况2:有用户声明的构造函数 -> 不生成默认构造函数
    DefaultConstructor(int val) : x(val) {}
    // 此时需要显式声明默认构造函数
    DefaultConstructor() = default;
    
    // 情况3:有const成员或引用成员需要初始化
    const int y = 0;  // 有默认成员初始化器 -> 可以生成默认构造函数
    // int& ref;       // 错误:引用必须初始化,默认构造函数无法生成
};

// 问题:依赖编译器生成可能导致未初始化
void test_default_ctor() {
    DefaultConstructor dc1;  // x未初始化!危险
    std::cout << dc1.x;      // 未定义行为
}

解决方法

// 方案1:显式声明=default并初始化成员
class SafeDefault {
    int x = 0;  // 提供默认值
    std::string s;
    
public:
    SafeDefault() = default;  // 现在安全了
    SafeDefault(int val) : x(val) {}
};

// 方案2:删除默认构造函数
class NoDefault {
    int x;
    
public:
    NoDefault() = delete;  // 禁止默认构造
    NoDefault(int val) : x(val) {}
};

2.2 拷贝构造函数

class CopyConstructor {
    int* data;
    size_t size;
    
public:
    CopyConstructor(size_t s) : size(s), data(new int[s]{}) {}
    
    // 问题:编译器生成的拷贝构造函数是浅拷贝
    // CopyConstructor(const CopyConstructor& other) : 
    //     data(other.data), size(other.size) {}
    // 这会导致双重释放!
    
    ~CopyConstructor() { delete[] data; }
};

// 正确做法:自定义拷贝构造函数
class ProperCopy {
    int* data;
    size_t size;
    
public:
    ProperCopy(size_t s) : size(s), data(new int[s]{}) {}
    
    // 自定义深拷贝
    ProperCopy(const ProperCopy& other) : 
        size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + other.size, data);
    }
    
    ~ProperCopy() { delete[] data; }
    
    // 一旦自定义了拷贝构造函数,移动构造函数就不会自动生成
};

2.3 拷贝赋值运算符

class CopyAssignment {
    std::vector<int> data;
    std::string name;
    
public:
    // 编译器生成的拷贝赋值运算符:
    // CopyAssignment& operator=(const CopyAssignment& other) {
    //     data = other.data;
    //     name = other.name;
    //     return *this;
    // }
    // 对于vector和string,这通常是安全的
    
    // 问题1:自赋值问题
    CopyAssignment& operator=(const CopyAssignment& other) {
        if (this == &other) return *this;  // 自赋值检查
        data = other.data;
        name = other.name;
        return *this;
    }
    
    // 问题2:异常安全问题
    CopyAssignment& safe_assign(const CopyAssignment& other) {
        // 拷贝和交换惯用法
        CopyAssignment temp(other);  // 可能抛异常
        swap(temp);                  // 不会抛异常
        return *this;
    }
    
    void swap(CopyAssignment& other) noexcept {
        using std::swap;
        swap(data, other.data);
        swap(name, other.name);
    }
};

2.4 移动构造函数和移动赋值运算符

class MoveOperations {
    std::unique_ptr<int[]> data;
    size_t size;
    
public:
    MoveOperations(size_t s) : size(s), data(std::make_unique<int[]>(s)) {}
    
    // 移动构造函数不会自动生成的条件:
    // 1. 用户声明了拷贝操作(构造或赋值)
    // 2. 用户声明了移动操作
    // 3. 用户声明了析构函数
    
    // 正确做法:显式声明移动操作
    MoveOperations(MoveOperations&& other) noexcept
        : data(std::move(other.data)), size(other.size) {
        other.size = 0;
    }
    
    MoveOperations& operator=(MoveOperations&& other) noexcept {
        if (this != &other) {
            data = std::move(other.data);
            size = other.size;
            other.size = 0;
        }
        return *this;
    }
    
    // 注意:声明了移动操作会抑制拷贝操作的生成
    // 需要显式声明拷贝操作(如果需要)
    MoveOperations(const MoveOperations& other) 
        : size(other.size), data(std::make_unique<int[]>(other.size)) {
        std::copy(other.data.get(), other.data.get() + size, data.get());
    }
};

2.5 析构函数

class DestructorIssues {
    int* data;
    
public:
    DestructorIssues() : data(new int[100]) {}
    
    // 问题:编译器生成的析构函数不会delete[]
    // ~DestructorIssues() {}  // 内存泄漏!
    
    // 正确:自定义析构函数
    ~DestructorIssues() {
        delete[] data;
    }
    
    // 重要:声明析构函数会抑制移动操作生成
    // 如果需要移动操作,必须显式声明
};

// 虚析构函数问题
class Base {
public:
    // 如果没有虚函数,不需要虚析构函数
    ~Base() = default;  // 非虚
    
    // 如果有虚函数,需要虚析构函数
    virtual void foo() {}
    virtual ~Base() = default;  // 应该是虚的
};

class Derived : public Base {
public:
    ~Derived() override {
        // 清理派生类资源
    }
};

3. Rule of Zero/Three/Five

3.1 Rule of Zero(现代C++首选)

// Rule of Zero:让编译器自动生成所有特殊成员函数
class RuleOfZero {
    // 使用管理资源的类(智能指针、容器等)
    std::unique_ptr<int[]> data;
    std::vector<int> items;
    std::string name;
    
public:
    // 不需要声明任何特殊成员函数
    // 编译器会自动生成正确的拷贝/移动操作
    
    RuleOfZero(std::string n, size_t count) 
        : name(std::move(n)), data(std::make_unique<int[]>(count)), items(count) {}
    
    // 编译器生成的函数:
    // 1. ~RuleOfZero()
    // 2. RuleOfZero(const RuleOfZero&)  // 调用成员的拷贝构造
    // 3. RuleOfZero(RuleOfZero&&)       // 调用成员的移动构造
    // 4. operator=(const RuleOfZero&)   // 调用成员的拷贝赋值
    // 5. operator=(RuleOfZero&&)        // 调用成员的移动赋值
};

3.2 Rule of Three(C++98传统)

// Rule of Three:如果需要自定义析构函数,也需要自定义拷贝构造和拷贝赋值
class RuleOfThree {
    char* data;
    size_t size;
    
public:
    RuleOfThree(const char* str) : size(std::strlen(str)) {
        data = new char[size + 1];
        std::strcpy(data, str);
    }
    
    // 1. 自定义析构函数
    ~RuleOfThree() {
        delete[] data;
    }
    
    // 2. 自定义拷贝构造函数
    RuleOfThree(const RuleOfThree& other) : size(other.size) {
        data = new char[size + 1];
        std::strcpy(data, other.data);
    }
    
    // 3. 自定义拷贝赋值运算符
    RuleOfThree& operator=(const RuleOfThree& other) {
        if (this != &other) {
            delete[] data;  // 释放原有资源
            size = other.size;
            data = new char[size + 1];
            std::strcpy(data, other.data);
        }
        return *this;
    }
    
    // 问题:没有移动操作,效率低
};

3.3 Rule of Five(C++11推荐)

// Rule of Five:自定义析构函数时,五个特殊成员函数都要考虑
class RuleOfFive {
    int* data;
    size_t size;
    
public:
    RuleOfFive(size_t s = 0) : size(s), data(size ? new int[size]{} : nullptr) {}
    
    // 1. 析构函数
    ~RuleOfFive() {
        delete[] data;
    }
    
    // 2. 拷贝构造函数
    RuleOfFive(const RuleOfFive& other) : size(other.size), 
                                          data(size ? new int[size] : nullptr) {
        if (data) {
            std::copy(other.data, other.data + size, data);
        }
    }
    
    // 3. 拷贝赋值运算符(使用拷贝和交换惯用法)
    RuleOfFive& operator=(RuleOfFive other) {  // 按值传递!
        swap(*this, other);
        return *this;
    }
    
    // 4. 移动构造函数
    RuleOfFive(RuleOfFive&& other) noexcept 
        : data(nullptr), size(0) {
        swap(*this, other);
    }
    
    // 5. 移动赋值运算符(通过拷贝赋值处理)
    // 已经通过operator=的按值传递版本处理了
    
    friend void swap(RuleOfFive& first, RuleOfFive& second) noexcept {
        using std::swap;
        swap(first.data, second.data);
        swap(first.size, second.size);
    }
};

4. 常见问题与陷阱

4.1 问题:析构函数抑制移动操作

class Problematic {
    std::vector<int> data;
    
public:
    Problematic() = default;
    
    // 用户声明的析构函数(即使是=default)
    virtual ~Problematic() = default;  // 这会抑制移动操作生成!
    
    // 结果:编译器不会生成移动构造函数和移动赋值运算符
    // 但是会生成拷贝操作
    
    // 意外行为:
    // Problematic p1;
    // Problematic p2 = std::move(p1);  // 调用拷贝构造函数,不是移动!
};

// 解决方法1:显式声明移动操作
class FixedWithExplicitMove {
    std::vector<int> data;
    
public:
    FixedWithExplicitMove() = default;
    
    // 显式声明所有特殊成员函数
    ~FixedWithExplicitMove() = default;
    FixedWithExplicitMove(const FixedWithExplicitMove&) = default;
    FixedWithExplicitMove& operator=(const FixedWithExplicitMove&) = default;
    
    // 关键:显式声明移动操作
    FixedWithExplicitMove(FixedWithExplicitMove&&) = default;
    FixedWithExplicitMove& operator=(FixedWithExplicitMove&&) = default;
};

// 解决方法2:使用final类(如果不是多态基类)
class FixedFinal final {
    std::vector<int> data;
    
public:
    // 如果不作为基类,不需要虚析构函数
    ~FixedFinal() = default;  // 非虚析构函数不会抑制移动操作
};

4.2 问题:const成员阻止移动赋值

class ConstMemberProblem {
    const int id;  // const成员!
    std::string name;
    
public:
    ConstMemberProblem(int i, std::string n) : id(i), name(std::move(n)) {}
    
    // 问题:const成员阻止默认移动赋值运算符生成
    // 因为const成员不能被赋值
    
    // 编译器不会生成:
    // ConstMemberProblem& operator=(ConstMemberProblem&&);
    
    // 但会生成拷贝赋值运算符?不会!因为const成员也不能被拷贝赋值
    // ConstMemberProblem& operator=(const ConstMemberProblem&) = delete;
};

// 解决方法:重新设计,避免const数据成员
class ConstMemberSolution {
    int id;
    std::string name;
    
public:
    ConstMemberSolution(int i, std::string n) : id(i), name(std::move(n)) {}
    
    // 提供const访问器
    int get_id() const { return id; }
    
    // 现在可以生成所有特殊成员函数
    // 或者使用=default显式声明
    ConstMemberSolution() = default;
    ~ConstMemberSolution() = default;
    ConstMemberSolution(const ConstMemberSolution&) = default;
    ConstMemberSolution(ConstMemberSolution&&) = default;
    ConstMemberSolution& operator=(const ConstMemberSolution&) = default;
    ConstMemberSolution& operator=(ConstMemberSolution&&) = default;
};

4.3 问题:引用成员阻止赋值操作

class ReferenceMemberProblem {
    int& ref;  // 引用成员
    std::string name;
    
public:
    ReferenceMemberProblem(int& r, std::string n) : ref(r), name(std::move(n)) {}
    
    // 问题:引用成员阻止默认拷贝/移动赋值运算符生成
    // 因为引用一旦初始化就不能重新绑定
    
    // 所有赋值运算符都被删除
};

// 解决方法1:使用指针代替引用
class ReferenceSolutionPointer {
    int* ptr;
    std::string name;
    
public:
    ReferenceSolutionPointer(int& r, std::string n) 
        : ptr(&r), name(std::move(n)) {}
    
    int& get() { return *ptr; }
    const int& get() const { return *ptr; }
    
    // 现在可以生成赋值运算符
    // 但要注意指针的语义
};

// 解决方法2:使用std::reference_wrapper
#include <functional>
class ReferenceSolutionWrapper {
    std::reference_wrapper<int> ref;
    std::string name;
    
public:
    ReferenceSolutionWrapper(int& r, std::string n) 
        : ref(r), name(std::move(n)) {}
    
    int& get() { return ref.get(); }
    const int& get() const { return ref.get(); }
    
    // std::reference_wrapper是可赋值的
    ReferenceSolutionWrapper& operator=(const ReferenceSolutionWrapper& other) {
        ref = other.ref;  // 重新绑定引用
        name = other.name;
        return *this;
    }
};

4.4 问题:默认函数与继承

class Base {
protected:
    std::string data;
    
public:
    Base() = default;
    virtual ~Base() = default;  // 虚析构函数
    
    // 声明虚析构函数抑制了移动操作生成
    Base(const Base&) = default;
    Base& operator=(const Base&) = default;
    
    // 需要显式声明移动操作
    Base(Base&&) = default;
    Base& operator=(Base&&) = default;
};

class Derived : public Base {
    std::vector<int> extra;
    
public:
    // 问题:派生类的特殊成员函数
    // 默认生成的派生类拷贝构造函数会调用基类的拷贝构造函数
    // 默认生成的派生类移动构造函数会调用基类的移动构造函数(如果有)
    
    // 但如果基类没有移动操作,派生类移动会退化为拷贝
    Derived(Derived&& other) 
        : Base(std::move(other)),  // 如果Base没有移动构造函数,调用拷贝构造函数
          extra(std::move(other.extra)) {}
    
    // 正确做法:确保基类有移动操作,或显式实现派生类移动操作
};

5. 高级技巧与模式

5.1 CRTP中的默认函数生成

template<typename Derived>
class CRTPBase {
protected:
    ~CRTPBase() = default;  // 保护析构函数,防止切片
    
public:
    // 提供通用的clone方法
    Derived clone() const {
        return Derived(static_cast<const Derived&>(*this));
    }
    
    // 注意:基类析构函数非虚,但CRTP是静态多态
};

class CRTPDerived : public CRTPBase<CRTPDerived> {
    std::vector<int> data;
    
public:
    CRTPDerived() = default;
    
    // 需要显式定义拷贝构造函数,因为基类拷贝构造函数不可访问
    CRTPDerived(const CRTPDerived& other) 
        : CRTPBase<CRTPDerived>(other), data(other.data) {}
    
    // 同样需要移动构造函数
    CRTPDerived(CRTPDerived&& other) noexcept
        : CRTPBase<CRTPDerived>(std::move(other)), 
          data(std::move(other.data)) {}
    
    // 赋值运算符
    CRTPDerived& operator=(CRTPDerived other) {
        swap(*this, other);
        return *this;
    }
    
    friend void swap(CRTPDerived& a, CRTPDerived& b) noexcept {
        using std::swap;
        swap(a.data, b.data);
    }
};

5.2 PIMPL模式中的默认函数

// 头文件
class PimplClass {
    class Impl;
    std::unique_ptr<Impl> pimpl;
    
public:
    // 特殊成员函数需要用户声明
    PimplClass();
    ~PimplClass();  // 需要在实现文件中定义,因为Impl是不完整类型
    
    // 移动操作可以默认
    PimplClass(PimplClass&&) noexcept = default;
    PimplClass& operator=(PimplClass&&) noexcept = default;
    
    // 拷贝操作需要自定义
    PimplClass(const PimplClass&);
    PimplClass& operator=(const PimplClass&);
    
    // 交换操作
    friend void swap(PimplClass& a, PimplClass& b) noexcept {
        a.swap(b);
    }
    
private:
    void swap(PimplClass& other) noexcept {
        using std::swap;
        swap(pimpl, other.pimpl);
    }
};

// 实现文件
class PimplClass::Impl {
    // 实现细节
public:
    std::vector<int> data;
    std::string name;
    
    Impl() = default;
    ~Impl() = default;
    Impl(const Impl&) = default;
    Impl(Impl&&) = default;
    Impl& operator=(const Impl&) = default;
    Impl& operator=(Impl&&) = default;
};

PimplClass::PimplClass() : pimpl(std::make_unique<Impl>()) {}

// 必须在Impl完整定义后定义
PimplClass::~PimplClass() = default;

PimplClass::PimplClass(const PimplClass& other) 
    : pimpl(other.pimpl ? std::make_unique<Impl>(*other.pimpl) : nullptr) {}

PimplClass& PimplClass::operator=(const PimplClass& other) {
    if (this != &other) {
        if (other.pimpl) {
            if (pimpl) {
                *pimpl = *other.pimpl;  // 拷贝赋值
            } else {
                pimpl = std::make_unique<Impl>(*other.pimpl);
            }
        } else {
            pimpl.reset();
        }
    }
    return *this;
}

5.3 使用concept约束默认函数(C++20)

template<typename T>
concept MoveEnabled = std::is_move_constructible_v<T> && 
                      std::is_move_assignable_v<T>;

template<typename T>
concept CopyEnabled = std::is_copy_constructible_v<T> && 
                      std::is_copy_assignable_v<T>;

template<typename T>
class SafeContainer {
    std::vector<T> data;
    
public:
    SafeContainer() = default;
    
    // 条件性生成特殊成员函数
    SafeContainer(const SafeContainer&) requires CopyEnabled<T> = default;
    SafeContainer& operator=(const SafeContainer&) requires CopyEnabled<T> = default;
    
    SafeContainer(SafeContainer&&) requires MoveEnabled<T> = default;
    SafeContainer& operator=(SafeContainer&&) requires MoveEnabled<T> = default;
    
    // 析构函数总是生成
    ~SafeContainer() = default;
    
    // 如果没有满足条件,构造函数被删除
};

6. 调试与检测技术

6.1 检测特殊成员函数的生成状态

#include <type_traits>

template<typename T>
void check_special_members() {
    std::cout << "Type: " << typeid(T).name() << "\n";
    std::cout << "Default constructible: " 
              << std::is_default_constructible_v<T> << "\n";
    std::cout << "Copy constructible: " 
              << std::is_copy_constructible_v<T> << "\n";
    std::cout << "Move constructible: " 
              << std::is_move_constructible_v<T> << "\n";
    std::cout << "Copy assignable: " 
              << std::is_copy_assignable_v<T> << "\n";
    std::cout << "Move assignable: " 
              << std::is_move_assignable_v<T> << "\n";
    std::cout << "Destructible: " 
              << std::is_destructible_v<T> << "\n";
    std::cout << "Trivially destructible: " 
              << std::is_trivially_destructible_v<T> << "\n";
    std::cout << "------------------------\n";
}

// 测试类
class TestClass {
public:
    TestClass() = default;
    ~TestClass() = default;
    
    // 注释/取消注释以下行来测试不同情况
    // TestClass(const TestClass&) = delete;
    // TestClass(TestClass&&) = delete;
};

void test_detection() {
    check_special_members<TestClass>();
}

6.2 使用静态断言确保正确性

template<typename T>
class ResourceWrapper {
    T* resource;
    
public:
    ResourceWrapper() : resource(nullptr) {}
    
    explicit ResourceWrapper(T* res) : resource(res) {}
    
    ~ResourceWrapper() {
        delete resource;
    }
    
    // 确保资源管理类不可拷贝
    ResourceWrapper(const ResourceWrapper&) = delete;
    ResourceWrapper& operator=(const ResourceWrapper&) = delete;
    
    // 但允许移动
    ResourceWrapper(ResourceWrapper&& other) noexcept 
        : resource(other.resource) {
        other.resource = nullptr;
    }
    
    ResourceWrapper& operator=(ResourceWrapper&& other) noexcept {
        if (this != &other) {
            delete resource;
            resource = other.resource;
            other.resource = nullptr;
        }
        return *this;
    }
    
    // 静态断言确保移动操作是noexcept
    static_assert(noexcept(ResourceWrapper(std::declval<ResourceWrapper&&>())),
                  "Move constructor must be noexcept");
    static_assert(noexcept(std::declval<ResourceWrapper&>() = 
                           std::declval<ResourceWrapper&&>()),
                  "Move assignment must be noexcept");
};

7. 最佳实践总结

7.1 现代C++默认函数生成指南

// 1. 优先使用Rule of Zero
class ModernClass {
    std::vector<int> data;           // 管理资源
    std::unique_ptr<Database> db;    // 管理资源
    std::string name;               // 管理资源
    
    // 不要声明任何特殊成员函数
    // 让编译器自动生成正确的版本
public:
    ModernClass(std::string n) : name(std::move(n)) {}
    
    // 编译器生成:
    // 1. 默认构造函数(因为成员都有默认初始化或默认构造函数)
    // 2. 析构函数
    // 3. 拷贝操作(深拷贝,调用成员的拷贝操作)
    // 4. 移动操作(高效移动,调用成员的移动操作)
};

// 2. 需要自定义行为时使用Rule of Five
class CustomBehavior {
    int* raw_ptr;
    size_t size;
    
public:
    explicit CustomBehavior(size_t sz) : size(sz), raw_ptr(new int[sz]{}) {}
    
    // 必须自定义析构函数
    ~CustomBehavior() { delete[] raw_ptr; }
    
    // 一旦自定义析构函数,必须考虑所有五个特殊成员函数
    CustomBehavior(const CustomBehavior& other) 
        : size(other.size), raw_ptr(new int[other.size]) {
        std::copy(other.raw_ptr, other.raw_ptr + size, raw_ptr);
    }
    
    CustomBehavior& operator=(CustomBehavior other) {
        swap(*this, other);
        return *this;
    }
    
    CustomBehavior(CustomBehavior&& other) noexcept 
        : raw_ptr(nullptr), size(0) {
        swap(*this, other);
    }
    
    // 移动赋值通过拷贝赋值处理
    
    friend void swap(CustomBehavior& a, CustomBehavior& b) noexcept {
        using std::swap;
        swap(a.raw_ptr, b.raw_ptr);
        swap(a.size, b.size);
    }
};

// 3. 使用=default显式声明意图
class ExplicitIntent {
    std::vector<int> data;
    
public:
    // 明确声明所有特殊成员函数
    ExplicitIntent() = default;
    ~ExplicitIntent() = default;
    
    // 明确拷贝语义
    ExplicitIntent(const ExplicitIntent&) = default;
    ExplicitIntent& operator=(const ExplicitIntent&) = default;
    
    // 明确移动语义(即使有析构函数)
    ExplicitIntent(ExplicitIntent&&) = default;
    ExplicitIntent& operator=(ExplicitIntent&&) = default;
    
    // 添加其他构造函数
    ExplicitIntent(std::initializer_list<int> init) : data(init) {}
};

// 4. 使用=delete禁止不需要的操作
class NonCopyable {
    std::unique_ptr<int> resource;
    
public:
    NonCopyable() = default;
    
    // 允许移动
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
    
    // 禁止拷贝
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

// 5. 对于多态基类,使用虚析构函数并显式声明移动操作
class PolymorphicBase {
protected:
    std::vector<int> base_data;
    
public:
    PolymorphicBase() = default;
    virtual ~PolymorphicBase() = default;  // 虚析构函数
    
    // 显式声明所有特殊成员函数
    PolymorphicBase(const PolymorphicBase&) = default;
    PolymorphicBase& operator=(const PolymorphicBase&) = default;
    
    // 关键:显式声明移动操作
    PolymorphicBase(PolymorphicBase&&) = default;
    PolymorphicBase& operator=(PolymorphicBase&&) = default;
    
    virtual void do_something() = 0;
};

7.2 决策流程图

开始设计类
│
├── 类管理资源吗?
│   ├── 否 → 使用Rule of Zero(让编译器生成一切)
│   └── 是 → 
│       ├── 资源可以被标准库类型管理吗?
│       │   ├── 是 → 使用标准库类型,回到Rule of Zero
│       │   └── 否 → 需要自定义资源管理
│       │       ├── 需要拷贝语义吗?
│       │       │   ├── 是 → 实现Rule of Five
│       │       │   └── 否 → 仅实现移动语义(删除拷贝操作)
│       │       └── 是继承层次的一部分吗?
│       │           ├── 是 → 确保基类有虚析构函数和移动操作
│       │           └── 否 → 不需要虚析构函数
│       └── 类有const/引用成员吗?
│           ├── 是 → 重新设计或自定义赋值操作
│           └── 否 → 正常实现
│
├── 类需要多态吗?
│   ├── 是 → 
│   │   ├── 基类:虚析构函数 + 显式声明移动操作
│   │   └── 派生类:确保正确处理基类部分
│   └── 否 → 不需要虚析构函数
│
└── 最终检查:
    ├── 所有特殊成员函数都正确声明了吗?
    ├── 移动操作是noexcept吗?
    ├── 自赋值安全吗?
    └── 异常安全吗?

通过遵循这些最佳实践和理解默认函数生成的规则,可以避免许多常见的内存管理错误和性能问题,编写出更安全、更高效的C++代码。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值