C++特殊成员函数生成规则
在C++中,特殊成员函数(special member functions)由编译器隐式声明并可能生成,包括默认构造函数、析构函数、拷贝/移动构造与赋值,以及新引入的默认比较运算符。这些函数的生成规则取决于用户声明、成员类型和基类。规则设计鼓励零规则(Rule of Zero):依赖编译器默认行为管理资源,避免自定义。

1. 特殊成员函数种类
C++20中共8种:
- 默认构造函数
T() noexcept; - 析构函数
~T(); - 拷贝构造函数
T(const T&); - 拷贝赋值运算符
T& operator=(const T&); - 移动构造函数
T(T&&) noexcept; - 移动赋值运算符
T& operator=(T&&) noexcept; - 默认等于运算符(C++20)
bool operator==(const T&) const; - 三路比较运算符(C++20)
std::strong_ordering operator<=>(const T&) const noexcept;
2. 生成规则详解(C++20)
2.1 默认构造函数
- 生成:无用户声明任何构造函数时生成。
- 行为:成员默认初始化(内置类型值初始化为零,类类型调用其默认ctor)。
- 抑制:用户声明任何ctor,或有不可默认构造成员/基类(const/引用无初始化器)。
- 控制:
= default;生成,= delete;禁止。
2.2 析构函数
- 生成:无用户声明时总是生成(trivial若可能)。
- 行为:逆序销毁成员/基类。
- noexcept:默认
noexcept(true)。 - 抑制:用户声明(含
= default)。 - 最佳:基类用虚析构
= default;。
2.3 拷贝构造函数
- 生成:无用户声明拷贝ctor、移动ctor或移动assign时。
- 行为:成员/基类逐拷贝。
- 抑制(C++11起):用户声明移动ctor/assign,或不可拷贝成员/基类。
- 控制:
= default/delete;。
2.4 拷贝赋值运算符
- 生成:无用户声明拷贝assign、移动ctor或移动assign时。
- 行为:成员/基类逐赋值(self-assign安全)。
- 抑制:用户声明移动ctor/assign,或不可赋值成员/基类。
- 控制:
= default/delete;。
2.5 移动构造函数
- 生成:无用户声明拷贝ctor、拷贝assign、移动ctor、移动assign或析构时。
- 行为:成员/基类逐移动(内置类型位拷贝)。
- 抑制:用户声明拷贝ctor/assign、移动assign、析构,或不可移动成员/基类。
- noexcept:若所有移动操作noexcept,则noexcept。
- 控制:
= default/delete;。
2.6 移动赋值运算符
- 生成:同移动ctor。
- 行为:释放旧资源,逐移动赋值。
- 抑制:同移动ctor。
- noexcept:同上。
- 控制:
= default/delete;。
2.7 默认等于运算符(C++20)
- 生成:无用户声明
==或<=>时。 - 行为:成员/基类逐
==(等价关系)。 - 签名:
bool operator==(const T&) const = default; - 抑制:不可==成员/基类(无公开==),或union有const/引用无brace-init成员。
- 控制:
= default;(若可能),= delete;。
2.8 三路比较运算符(C++20)
- 生成:无用户声明
<=>、==、operator<、operator>等6个比较时。 - 行为:成员/基类逐
<=>,返回strong_ordering(若异质则partial_ordering等)。 - 签名:
strong_ordering operator<=>(const T&) const noexcept = default; - 抑制:不可
<=>成员/基类,或有异质比较需求。 - 隐式生成:
<=>隐式生成==、!=、<、>、<=、>=。 - 控制:
= default;(指定strong_ordering/partial_ordering/weak_ordering),= delete;。
3. 生成规则总结表
| 用户定义了 | 默认构造 | 析构函数 | 拷贝构造 | 拷贝赋值 | 移动构造 | 移动赋值 |
|---|---|---|---|---|---|---|
| 无 | 自动生成 | 自动生成 | 自动生成 | 自动生成 | 自动生成 | 自动生成 |
| 默认构造 | - | 自动生成 | 自动生成 | 自动生成 | 自动生成 | 自动生成 |
| 析构函数 | 自动生成 | - | 自动生成* | 自动生成* | 不生成 | 不生成 |
| 拷贝构造 | 不生成 | 自动生成 | - | 自动生成* | 不生成 | 不生成 |
| 拷贝赋值 | 自动生成 | 自动生成 | 自动生成* | - | 不生成 | 不生成 |
| 移动构造 | 不生成 | 自动生成 | 已删除 | 已删除 | - | 不生成 |
| 移动赋值 | 自动生成 | 自动生成 | 已删除 | 已删除 | 不生成 | - |
注:* 表示该行为已在标准中标记为弃用(Deprecated),未来版本可能会改变。
4. C++20 自动比较运算符生成
C++20 引入了 operator== 和 operator<=> 的自动生成机制,极大地简化了类类型的比较逻辑。
4.1 默认等于运算符 (operator==)
- 自动重写:只需定义
operator==,编译器会自动推导出operator!=。 - 重写规则:
a != b会被重写为!(a == b)。 - 成员逐位比较:
= default实现会按声明顺序递归比较所有基类和成员。
4.2 三路比较运算符 (operator<=>)
- 一箭六雕:定义
operator<=>后,编译器会自动生成==,!=,<,>,<=,>=。 - 返回类型:
std::strong_ordering:所有成员都支持强序(如int)。std::partial_ordering:包含浮点数等只支持偏序的成员。
- 性能优化:建议同时
defaultoperator==。虽然<=>能推导出==,但显式的==通常能提供更高效的相等性检查(如提前跳过长度不等的容器)。
4.3 Tips
- 不要在 Union 中使用:编译器无法确定 union 的活跃成员,自动生成会被删除。
- 成员顺序即逻辑:比较逻辑严格依赖成员声明顺序。改变顺序可能破坏向后兼容性。
- 基类约束:若基类不支持比较,子类的
= default将被隐式删除。
5. C++20 变化与最佳实践
- Rule of Zero:优先标准容器(如
unique_ptr),零自定义特殊函数。 - Rule of Five:自定义资源管理时定义析构+4拷贝/移动。
- noexcept:移动操作标
noexcept提升容器性能(vector resize等)。 - 删除传播:不可拷贝成员使类不可拷贝。
- 简洁:避免边界case,用
= default/delete明确意图。复杂继承/资源用组合。
5. 示例
#include <iostream>
#include <compare> // std::strong_ordering
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 生成所有比较
};
struct Movable {
int* ptr;
Movable() = default;
~Movable() { delete ptr; }
Movable(const Movable&) : ptr(new int(*other.ptr)) {} // 自定义拷贝
Movable& operator=(const Movable&) { /* impl */ return *this; }
// 移动隐式删除,因自定义析构/拷贝
Movable(Movable&&) = default; // 显式启用
};
int main() {
Point p1{1,2}, p2{1,2};
std::cout << (p1 == p2) << '\n'; // true,隐式==
Movable m; // 默认ctor
return 0;
}6. 常见陷阱
- 移动被抑制:自定义析构/拷贝→无隐式移动,标
= default;。 - 比较union:const/引用成员需小心。
- 异质比较:
<=>需指定partial_ordering。 - 虚函数:有虚析构→比较非const。
此规则确保零开销抽象,遵循KISS:简单胜于复杂。



