章节目录

第22章总结

本节阅读量:

智能指针类是组合类,用于管理动态分配的内存,并确保智能指针对象超出作用域时删除内存。

复制语义允许复制类对象。这主要通过拷贝构造函数和拷贝赋值运算符完成。

移动语义意味着类会转移对象的所有权,而不是制作副本。这主要通过移动构造函数和移动赋值运算符完成。

std::auto_ptr已弃用,应避免使用。

右值引用是被设计为使用右值初始化的引用。使用“双&”符号创建右值引用。编写采用右值引用参数的函数是可以的,但您几乎不应该返回右值引用。

如果我们构造一个对象或进行赋值,而参数是左值,那么唯一合理的做法就是复制这个左值。我们不能假设更改左值是安全的,因为它可能会在后续程序中再次使用。如果有一个表达式“a=b”,且b是左值,就不应期望b以任何方式改变。

然而,如果我们构造一个对象或进行赋值,而参数是右值,那么我们知道右值只是某种临时对象。我们可以简单地将其资源(代价很低)转移到正在构造或赋值的对象,而不是复制它(这可能很昂贵)。这样做是安全的,因为临时对象无论如何都会在表达式末尾被销毁,所以我们知道它不会再次被使用!

可以使用delete关键字通过删除拷贝构造函数和拷贝赋值运算符来禁用类的复制语义。

std::move允许您将左值视为右值。当我们想对左值调用移动语义而不是复制语义时,这很有用。

std::unique_ptr是您最可能使用的智能指针类。它管理单个不可共享的资源。应首选std::make_unique()(自C++14起)来创建新的std::unique_ptr。std::unique_ptr禁用复制语义。

std::shared_ptr是在需要多个对象访问同一资源时使用的智能指针类。在管理资源的最后一个std::shared_ptr被销毁之前,该资源不会被销毁。应首选std::make_shared()来创建新的std::shared_ptr。对于std::shared_ptr,应该使用复制语义来创建指向同一对象的其他std::shared_ptr。

std::weak_ptr是在您需要一个或多个对象查看和访问由std::shared_ptr管理的资源时使用的智能指针类。与std::shared_ptr不同,在判断是否应销毁资源时,不会考虑std::weak_ptr。


习题

问题 1

说明应当在什么情况下使用下列各种指针。

1a) std::unique_ptr

查看答案

当你希望用一个智能指针管理某个不会被共享的动态对象时,应当使用std::unique_ptr。

1b) std::shared_ptr

查看答案

当你希望用一个智能指针管理某个可能被共享的动态对象时,应当使用std::shared_ptr。直到所有持有该对象的std::shared_ptr都被销毁,这个对象才会被释放。

1c) std::weak_ptr

查看答案

当你希望访问某个由std::shared_ptr管理的对象,但又不希望把自己的生命周期与该std::shared_ptr绑定在一起时,应当使用std::weak_ptr。

1d) std::auto_ptr

查看答案

std::auto_ptr已被弃用,并在C++17中被移除,不应当再使用。

问题 2

说明为什么移动语义是围绕右值展开的。

查看答案

因为右值是临时的,我们知道它们在使用之后就会被销毁。按值传递或返回右值时,先拷贝再销毁原对象是一种浪费。相反,我们可以直接移动(窃取)右值的资源,这通常更高效。

问题 3

下面这段代码有什么问题?请把程序改成符合最佳实践的写法。

3a)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <memory> // 用于 std::shared_ptr

class Resource
{
public:
	Resource() { std::cout << "Resource acquired\n"; }
	~Resource() { std::cout << "Resource destroyed\n"; }
};

int main()
{
	auto* res{ new Resource{} };
	std::shared_ptr<Resource> ptr1{ res };
	std::shared_ptr<Resource> ptr2{ res };

	return 0;
}
查看答案

ptr2是由res而不是由ptr1创建的。这意味着现在有两个std::shared_ptr各自独立地试图管理这个Resource(它们彼此并不知道对方)。当其中一个离开作用域时,另一个就会持有悬空指针。

ptr2应当改为用ptr1初始化,而不是用res。这样ptr1和ptr2就能共享res的所有权。同时应当使用std::make_shared(),而不是手工动态分配。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <iostream>
#include <memory> // 用于 std::shared_ptr

class Resource
{
public:
	Resource() { std::cout << "Resource acquired\n"; }
	~Resource() { std::cout << "Resource destroyed\n"; }
};

int main()
{
	auto ptr1{ std::make_shared<Resource>() };
	auto ptr2{ ptr1 };

	return 0;
}

22.6 std::shared_ptr的循环依赖性问题和std::weak_ptr

上一节

23.0 对象关系

下一节