C++何时会阻止默认的特殊成员函数的生成

在C++中,编译器会根据类的定义情况自动决定是否生成默认的特殊成员函数(如构造函数、拷贝/移动操作、析构函数)。

1. 用户显式声明相关成员函数

  • 显式声明或删除某个函数

    如果用户显式声明(即使使用 =default=delete)某个特殊成员函数,编译器将不再生成默认版本。例如:

1
2
3
4
5
6
class Example {
public:
    Example() = default;          // 允许生成默认构造函数
    Example(const Example&) {}    // 用户定义的拷贝构造函数
    // 编译器不再生成默认的移动构造函数和移动赋值运算符
};

2. 用户定义析构函数、拷贝/移动操作的影响

  • 定义析构函数

    如果用户定义了析构函数(即使为空),编译器会删除默认的移动操作(移动构造函数和移动赋值运算符),但拷贝操作仍可能生成(除非其他条件阻止)。

1
2
3
4
5
class Example {
public:
	~Example() {}  // 用户定义的析构函数
	// 移动操作被隐式删除,拷贝操作可能生成(若无其他限制)
};
  • 定义拷贝操作

    如果用户定义了拷贝构造函数或拷贝赋值运算符,编译器会删除默认的移动操作

1
2
3
4
5
class Example {
public:
  Example(const Example&) {}  // 用户定义的拷贝构造函数
  // 移动操作被隐式删除
};
  • 定义移动操作

    如果用户定义了移动构造函数或移动赋值运算符,编译器会删除默认的拷贝操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <utility>
#include <string>
#include <iostream>

struct Example {
  	std::string str;

    Example() = default;

	Example(const std::string& s): str(s) {
    	std::cout << "Example()\n";
    }
  
  	Example(Example&& other) {
    	str = std::move(other.str);
      	std::cout << "Example(Example&&)\n";
    }

    Example& operator=(Example&& other) {
    	str = std::move(other.str);
      	std::cout << "operator=(Example&&)\n";
        return *this;
    }
};

int main() {
	Example x1("Hello");
	Example x2 = std::move(x1);
    Example x3;
    x3 = std::move(x2);
}

执行结果为:

1
2
3
Example()
Example(Example&&)
operator=(Example&&)

3. 类成员或基类的限制

  • 不可默认构造/拷贝/移动的成员

    如果类中包含无法默认构造、拷贝或移动的成员(如 std::unique_ptr、带有删除拷贝操作的类),则对应的默认特殊成员函数会被隐式删除

1
2
3
4
class Example {
    std::unique_ptr<int> ptr;  // 不可拷贝
};
// 默认的拷贝构造函数和拷贝赋值运算符被删除
  • 基类或成员的特殊成员函数被删除

    如果基类或成员的某个特殊成员函数被删除或不可访问,派生类对应的函数也会被隐式删除

1
2
3
4
5
6
7
8
class NonCopyable {
public:
    NonCopyable(const NonCopyable&) = delete;
};

class Derived : public NonCopyable {
    // 拷贝构造函数被隐式删除,因为基类的拷贝构造函数被删除
};
使用 Hugo 构建
主题 StackJimmy 设计