doc/docs/Cpp-Notes-main/Cpp-Primer/13.拷贝控制.md
sairate b5160be045 docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-06 16:14:30 +08:00

124 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第十三章 拷贝控制
**拷贝控制操作**copy control:
- 拷贝构造函数copy constructor
- 拷贝赋值运算符copy-assignment operator
- 移动构造函数move constructor
- 移动赋值函数move-assignement operator
- 析构函数destructor
## 拷贝、赋值和销毁
### 拷贝构造函数
- 如果一个构造函数的第一个参数是**自身类类型的引用**,且任何额外参数都有默认值,则此构造函数是**拷贝构造函数**。
- `class Foo{ public: Foo(const Foo&); }`
- **合成的拷贝构造函数**synthesized copy constructor会将参数的成员逐个拷贝到正在创建的对象中。
- **拷贝初始化**
- 将右侧运算对象拷贝到正在创建的对象中,如果需要,还需进行类型转换。
- 通常使用拷贝构造函数完成。
- `string book = "9-99";`
- 出现场景:
-`=`定义变量时。
- 将一个对象作为实参传递给一个非引用类型的形参。
- 从一个返回类型为非引用类型的函数返回一个对象。
- 用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。
### 拷贝赋值运算符
- **重载赋值运算符**
- 重写一个名为`operator=`的函数.
- 通常返回一个指向其左侧运算对象的引用。
- `Foo& operator=(const Foo&);`
- **合成拷贝赋值运算符**
- 将右侧运算对象的每个非`static`成员赋予左侧运算对象的对应成员。
### 析构函数
- 释放对象所使用的资源,并销毁对象的非`static`数据成员。
- 名字由波浪号接类名构成。没有返回值,也不接受参数。
- `~Foo();`
- 调用时机:
- 变量在离开其作用域时。
- 当一个对象被销毁时,其成员被销毁。
- 容器被销毁时,其元素被销毁。
- 动态分配的对象,当对指向它的指针应用`delete`运算符时。
- 对于临时对象,当创建它的完整表达式结束时。
- **合成析构函数**
- 空函数体执行完后,**成员会被自动销毁。**
- 注意:析构函数体本身并不直接销毁成员。
### 三/五法则
- 需要析构函数的类也需要拷贝和赋值操作。
- 需要拷贝操作的类也需要赋值操作,反之亦然。
### 使用=default
- 可以通过将拷贝控制成员定义为`=default`来显式地要求编译器生成合成的版本。
- 合成的函数将隐式地声明为内联的。
### 阻止拷贝
- 大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符,无论是隐式地还是显式地。
- 定义删除的函数:`=delete`
- 虽然声明了它们,但是不能以任何方式使用它们。
- 析构函数不能是删除的成员。
- 如果一个类有数据成员不能默认构造、拷贝、复制或者销毁,则对应的成员函数将被定义为删除的。
- 老版本使用`private`声明来阻止拷贝。
## 拷贝控制和资源管理
- 类的行为可以像一个值,也可以像一个指针。
- 行为像值:对象有自己的状态,副本和原对象是完全独立的。
- 行为像指针:共享状态,拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。
## 交换操作
- 管理资源的类通常还定义一个名为`swap`的函数。
- 经常用于重排元素顺序的算法。
-`swap`而不是`std::swap`
## 对象移动
- 很多拷贝操作后,原对象会被销毁,因此引入移动操作可以大幅度提升性能。
- 在新标准中,我们可以用容器保存不可拷贝的类型,只要它们可以被移动即可。
- 标准库容器、`string``shared_ptr`类既可以支持移动也支持拷贝。`IO`类和`unique_ptr`类可以移动但不能拷贝。
### 右值引用
- 新标准引入右值引用以支持移动操作。
- 通过`&&`获得右值引用。
- 只能绑定到一个将要销毁的对象。
- 常规引用可以称之为左值引用。
- 左值持久,右值短暂。
**move函数**
- `int &&rr2 = std::move(rr1);`
- `move`告诉编译器,我们有一个左值,但我希望像右值一样处理它。
- 调用`move`意味着:除了对`rr1`赋值或者销毁它外,我们将不再使用它。
### 移动构造函数和移动赋值运算符
- **移动构造函数**
- 第一个参数是该类类型的一个引用,关键是,这个引用参数是一个**右值引用**。
- `StrVec::StrVec(StrVec &&s) noexcept{}`
- 不分配任何新内存,只是接管给定的内存。
- **移动赋值运算符**
- `StrVec& StrVec::operator=(StrVec && rhs) noexcept{}`
- 移动右值,拷贝左值。
- 如果没有移动构造函数,右值也被拷贝。
- 更新三/五法则:如果一个类定义了任何一个拷贝操作,它就应该定义所有五个操作。
- 移动迭代器:
- `make_move_iterator`函数讲一个普通迭代器转换为一个移动迭代器。
- 建议:小心地使用移动操作,以获得性能提升。
### 右值引用和成员函数
- 区分移动和拷贝的重载函数通常有一个版本接受一个`const T&`,而另一个版本接受一个`T&&`
- 引用限定符:
- 在参数列表后面防止一个`&`,限定只能向可修改的左值赋值而不能向右值赋值。