C++ 类编译器默认生成的十大特殊成员函数

C++ 类编译器默认生成的十大特殊成员函数

一、完整列表(C++98 ~ C++11 统一标准)

如果类没有手动声明对应函数,编译器会隐式自动生成:

  1. 默认构造函数(无参构造)
  2. 拷贝构造函数(复制构造)
  3. 拷贝赋值运算符 operator=
  4. 析构函数
  5. 移动构造函数(C++11 新增)
  6. 移动赋值运算符 operator=(C++11 新增)

前4个是C++98就有,后2个移动语义是C++11引入。

二、逐个详解生成规则、触发条件、禁用场景

1. 默认构造函数 T()

生成条件

没有任何自定义构造函数(有参、无参都算自定义),编译器生成空无参构造。

struct A {
    int x;
};
// 编译器生成:A::A() {}
A a; // 合法调用默认构造
不生成的情况

只要写了任意构造函数(哪怕带参数),编译器不再生成默认构造:

struct A {
    A(int) {} // 自定义构造,无默认构造
};
A a; // 编译报错
特殊限制
  • 类含const成员/引用成员:自动默认构造函数会被删除(无法初始化)。
  • 成员无默认构造:本类默认构造被删除。

2. 拷贝构造函数 T(const T&)

生成条件

没有手动声明拷贝构造,编译器生成浅拷贝版本:逐成员拷贝(值拷贝)。

struct A { int x; };
// 自动生成 A(const A& other) : x(other.x) {}
A a1;
A a2 = a1; // 调用拷贝构造
不生成/被删除场景
  1. 手动写了拷贝构造;
  2. 类包含不可拷贝成员(引用、const成员、unique_ptr、无拷贝构造的成员);
  3. 基类拷贝构造被删除/私有。

3. 拷贝赋值 T& operator=(const T&)

生成规则

无手动重载赋值,生成逐成员赋值浅拷贝。

A a1, a2;
a2 = a1; // 调用自动拷贝赋值
自动删除场景
  • 有 const / 引用成员(引用不能重新赋值);
  • 成员不可赋值(unique_ptr等);
  • 基类赋值运算符被删除。

4. 析构函数 ~T()

生成条件

没有手动写析构,编译器生成空析构:

struct A { int x; };
// 自动生成 ~A(){},对象销毁时调用
特殊规则
  1. 哪怕写了其他构造,析构依然会默认生成;
  2. 析构不会被自动删除,只有手动 ~A() = delete; 才禁用;
  3. 基类析构建议加 virtual,否则多态释放内存泄漏(编译器不会自动virtual)。

5. 移动构造 T(T&&) C++11

生成前提(全部满足才生成)
  1. 没有自定义:拷贝构造、拷贝赋值、移动构造、移动赋值、析构;
  2. 所有非静态成员、基类都支持移动构造。
作用

转移资源所有权(std::move),性能优于拷贝。

触发删除

只要手动写了拷贝构造/析构等任一函数,编译器不再生成移动构造


6. 移动赋值 T& operator=(T&&) C++11

生成规则和移动构造完全一致:只要手动定义拷贝/析构等,就不会自动生成。

三、记忆速记:三/五法则

1. C++98 三法则(Rule of Three)

如果你手动定义了下面任意一个,建议手动写出另外两个:

  • 析构函数
  • 拷贝构造
  • 拷贝赋值运算符

原因:通常是管理堆内存/资源,默认浅拷贝会双重释放、内存泄漏。

2. C++11 五法则(Rule of Five)

扩展两条移动函数:
手动定义析构/拷贝构造/拷贝赋值任一,编译器不会自动生成移动构造、移动赋值;
此时资源类建议显式定义/禁用全部5个特殊函数:

  1. 默认构造
  2. 拷贝构造
  3. 拷贝赋值
  4. 移动构造
  5. 移动赋值
  6. 析构

四、示例:全部显式声明/禁用

class Demo {
public:
    // 1. 默认构造
    Demo() = default;
    // 2. 拷贝构造
    Demo(const Demo&) = delete;
    // 3. 拷贝赋值
    Demo& operator=(const Demo&) = delete;
    // 4. 移动构造
    Demo(Demo&&) = default;
    // 5. 移动赋值
    Demo& operator=(Demo&&) = default;
    // 6. 析构
    ~Demo() = default;
};

五、补充:不自动生成的常用运算符(容易混淆)

下面这些不会由编译器默认生成,必须手动重载:

  • operator+operator- 算术运算符
  • operator==operator< 比较运算符
  • operator[]operator()operator*
  • 流运算符 << / >>

只有上面6个特殊成员函数是编译器按需隐式生成。

另外四个

一、先说结论

编译器会隐式生成两个取地址重载,但它们不属于「六大特殊成员函数」,平时讲默认生成的特殊函数一般不提:

  1. 普通取地址:T* operator&()
  2. const取地址:const T* operator&() const

1. 自动生成规则

只要你没有手动重载 operator&,编译器就会自动生成这两个版本:

struct A {
    int x;
};
// 编译器隐式生成等价代码:
A* operator&() { return this; }
const A* operator&() const { return this; }

作用:直接返回当前对象 this 指针。

使用示例

A a;
A* p = &a;      // 调用 operator&()
const A ca{};
const A* cp = &ca; // 调用 const 版本

2. 为什么平时讲默认函数很少提它?

(1)标准定义区分两类成员

C++标准里把成员函数分成两类:

  1. 特殊成员函数(special member functions)
    默认构造、拷贝构造、拷贝赋值、析构、移动构造、移动赋值(共6个)
    这一组有严格的生成/抑制规则(三/五法则只针对它们)。

  2. 内置运算符成员(如 operator&)
    operator&operator->operator[] 等不属于特殊成员;
    只要不手动写,就自动生成,不受三/五法则影响

(2)几乎没人重载 operator&

正常业务代码完全不需要改写取地址;
只有少数黑魔法库(智能指针代理、侵入式容器、调试追踪内存)才会手动重载 operator&,属于非常规操作。

3. 手动重载后,编译器不再生成默认版本

一旦自己写任意一个 operator&,编译器就不会再生成默认的两个版本:

struct A {
    // 手动重载取地址,编译器不再生成默认的 &
    int* operator&() {
        return &x;
    }
    int x;
};

A a;
&A; // 调用自定义版本,返回 int*,不再返回 A*
const A ca{};
&ca; // 编译报错:没有 const 版 operator&

4. 补充:还有两个配套默认生成的一元运算符

除了 operator&,编译器还会隐式生成:

  • operator->(箭头):只有类是智能指针/包装器风格才会用到,普通类不会触发使用
  • operator*(解引用):同样极少重载

总结对比

  1. 六大特殊成员函数(考试/面试必考):默认构造、拷贝构造、拷贝赋值、析构、移动构造、移动赋值;受三/五法则约束。
  2. 默认生成但不属于特殊成员operator&() / operator&() const;不受三/五法则约束,教材一般不列入“默认生成函数”清单。

面试如果问:“C++ 类编译器默认生成哪些成员函数?”
优先答6个特殊成员;如果面试官追问有没有其他默认生成的运算符,再补充取地址重载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值