C++ 中由编译器默认生成的类方法

在类中,如果你未显示定义下列一些函数,编译器会帮你自动生成它们的默认版本。


📌 一、编译器默认生成的六大函数

函数名称 触发条件 C++标准支持
默认构造函数 用户未定义任何构造函数时生成 C++98
析构函数 用户未定义时自动生成 C++98
复制构造函数 用户未定义时生成浅拷贝版本 C++98
复制赋值运算符 用户未定义时生成浅拷贝版本 C++98
移动构造函数 用户未定义拷贝控制成员时生成 C++11
移动赋值运算符 用户未定义拷贝控制成员时生成 C++11

🔍 二、各函数处理成员变量的机制

1. 默认构造函数

ClassName() = default;
  • 内置类型:不初始化(保留内存原始值)[1]
  • 类类型成员:递归调用其默认构造函数
  • 继承体系:调用基类默认构造函数

2. 复制构造函数

ClassName(const ClassName& src) 
    : member1(src.member1), 
      member2(src.member2) {}
  • 基本类型:直接拷贝值
  • 类类型成员:调用其复制构造函数
  • 指针成员:仅复制指针地址(浅拷贝)

3. 复制赋值运算符

ClassName& operator=(const ClassName& src) {
    member1 = src.member1;
    member2 = src.member2;
    return *this;
}
  • 基本类型:直接赋值
  • 类类型成员:调用其赋值运算符
  • 指针成员:仅复制指针(导致悬空指针)

4. 析构函数

~ClassName() {}
  • 类类型成员:递归调用其析构函数
  • 指针成员:不执行delete操作(内存泄漏风险)

5. 移动构造函数(C++11)

ClassName(ClassName&& src) 
    : member1(std::move(src.member1)),
      member2(std::move(src.member2)) {}
  • 基本类型:直接转移值
  • 类类型成员:调用其移动构造函数
  • 指针成员:转移所有权(源指针置空)

6. 移动赋值运算符(C++11)

ClassName& operator=(ClassName&& src) {
    member1 = std::move(src.member1);
    member2 = std::move(src.member2);
    return *this;
}

⚠️ 三、关键风险与最佳实践

风险案例:浅拷贝陷阱

class String {
    char* data; // 动态分配内存
public:
    // 默认生成的复制构造函数会导致双重释放
};

必须自定义的场景:

  1. 类包含指针成员(需深拷贝)
  2. 类管理非POD类型资源(文件句柄等)
  3. 需要维护对象计数等特殊逻辑

三法则(Rule of Three):

  • 如果定义了以下任一函数,通常需要同时定义:
    • 析构函数
    • 复制构造函数
    • 复制赋值运算符

五法则(C++11扩展):

  • 增加移动构造函数和移动赋值运算符的定制需求

📚 四、验证示例代码

#include <iostream>
using namespace std;

class Test {
    int* ptr;
public:
    Test() : ptr(new int(10)) {} // 默认构造
    ~Test() { delete ptr; }      // 必须自定义析构
  
    void show() const { 
        cout << "Value: " << *ptr << endl; 
    }
};

int main() {
    Test a;
    Test b = a;  // 默认复制构造导致双重释放!
    a.show();
    return 0;
}


  1. 根据C++标准,内置类型在默认构造函数中不进行初始化 ↩︎

posted @ 2025-04-17 15:16  thammer  阅读(41)  评论(0)    收藏  举报