C++ 类和对象(二):默认成员函数详解

C++ 类和对象(二):默认成员函数详解

在 C++ 中,当你定义一个类时,即使你什么都不写,编译器也会为你自动生成一些特殊的成员函数,这些函数被称为默认成员函数(也叫合成成员函数隐式生成的特殊成员函数)。

C++11 之后,这些默认生成的函数行为更加清晰和可控,理解它们是掌握 C++ 类语义、资源管理、移动语义等现代 C++ 特性的关键。

一、C++ 类会自动生成的 6 个默认特殊成员函数

序号函数名称作用简述是否默认生成是否 noexcept(默认)是否 trivial(平凡)可能是否可以被 =default / =delete
1默认构造函数X()是(条件)可以
2拷贝构造函数X(const X&)是(条件)可以
3拷贝赋值运算符X& operator=(const X&)是(条件)可以
4移动构造函数X(X&&)是(条件)是(条件)可以
5移动赋值运算符X& operator=(X&&)是(条件)是(条件)可以
6析构函数~X()是(条件)是(条件)可以

二、每个默认成员函数的生成规则(C++11/14/17/20)

1. 默认构造函数 X::X()

什么时候生成?

  • 用户没有声明任何构造函数时,编译器会生成一个无参默认构造函数
  • 如果用户声明了任何带参数的构造函数,则不再生成默认构造函数

默认行为

  • 对类类型的成员:调用成员的默认构造函数
  • 对基本类型成员:不做任何初始化(值不确定)
class A {
    int x;          // 未初始化
    string s;       // 调用 string()
    vector<int> v;  // 调用 vector()
};

A a;  // x 是未定义值,s 和 v 是空对象

现代建议

  • 显式写 = default= delete
  • 优先使用成员初始化列表,避免未初始化问题

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

生成条件

  • 用户没有声明任何拷贝构造函数时,编译器自动生成。
  • 生成的版本是成员逐个拷贝(member-wise copy)。

默认行为

  • 基本类型成员:值拷贝
  • 类类型成员:调用该成员的拷贝构造函数
class Point {
    int x, y;
public:
    Point(const Point& other) = default;  // 编译器会生成逐成员拷贝
};

3. 拷贝赋值运算符 X& X::operator=(const X&)

生成条件

  • 用户没有声明任何拷贝赋值运算符时生成。
  • 默认行为:成员逐个赋值,并返回 *this

注意

  • 如果类有 const 或引用成员,默认拷贝赋值会被抑制(无法生成)。

4. 移动构造函数 & 移动赋值运算符(C++11 引入)

生成条件(非常重要)

编译器只在以下三种情况都满足时才会生成移动操作:

  1. 用户没有声明任何拷贝构造函数、拷贝赋值、移动构造函数、移动赋值、析构函数。
  2. 用户没有声明任何纯虚函数(即不是抽象类)。
  3. 移动操作的隐式声明没有被显式删除(=delete)。

最常见导致不生成移动语义的情况

  • 声明了析构函数
  • 声明了拷贝构造 / 拷贝赋值
  • 声明了其中一个移动函数(另一个通常也会被抑制)
class MyString {
    char* str;
public:
    ~MyString() { delete[] str; }           // 声明了析构 → 移动被抑制
    // 此时编译器不会生成移动构造和移动赋值
};

现代推荐写法(Rule of Five / Rule of Zero):

// 最佳实践:要么全定义,要么全默认(Rule of Zero)
class Widget {
public:
    Widget() = default;
    Widget(const Widget&) = default;
    Widget& operator=(const Widget&) = default;
    Widget(Widget&&) = default;
    Widget& operator=(Widget&&) = default;
    ~Widget() = default;
};

5. 析构函数 ~X()

生成条件

  • 用户没有声明析构函数时,编译器生成一个默认析构函数
  • 默认行为:对每个成员调用其析构函数。

特殊情况

  • 如果类有虚函数,默认析构函数是虚析构
  • 如果用户声明了析构函数(无论是否 =default),都会抑制移动操作的生成

三、六大默认成员函数的生成规则总结表(非常重要)

你声明了这些成员中的任何一个默认构造函数拷贝构造拷贝赋值移动构造移动赋值析构函数
什么都没声明生成生成生成生成生成生成
声明了任何构造函数不生成生成生成生成生成生成
声明了拷贝构造生成生成不生成不生成生成
声明了拷贝赋值生成生成不生成不生成生成
声明了移动构造不生成生成生成不生成生成
声明了移动赋值不生成生成生成不生成生成
声明了析构函数生成生成生成不生成不生成

四、现代 C++ 推荐的写法模式(2024-2025 主流)

  1. Rule of Zero(最推荐)
    不写任何特殊成员函数,让编译器自动生成(适合资源管理完全交给成员的类)
  2. Rule of Five
    如果你需要自定义其中一个,就把五个都显式声明(通常配合 =default 或实现)
  3. Rule of Four + Move(折中)
    自定义析构 + 拷贝构造 + 拷贝赋值 + 移动构造 + 移动赋值(较老风格)

五、经典面试问题

  1. 声明了析构函数后,移动构造还会生成吗?为什么?
  2. 什么时候编译器不会生成默认构造函数?
  3. 什么是 trivial 的默认构造函数?有什么用?
  4. 如果一个类有引用成员或 const 成员,会影响哪些默认函数的生成?
  5. 如何让一个类只能移动不能拷贝?(最常见写法是什么?)

如果你想看具体代码示例、某个规则的详细实验、或者“如何设计一个支持完美转发的类”,可以告诉我,我可以继续深入展开讲解。

文章已创建 4580

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部