explorer

万丈高楼平地起,勿在浮沙筑高台

0%

[What] Effective Modern C++ :使用 weak_ptr 来检查 shared_ptr 资源是否已经释放

std::weak_ptr附属于shared_ptrweak_ptr并不会影响资源引用计数的增加和减小,当最后一个shared_ptr被销毁时,资源便会被释放,就算现在依然存在weak_ptr

weak_ptr并不能够被解引用,它的目的主要是为了探测shared_ptr之间所指向的资源是否已经释放。

其应用场景一般是在当需要新建一个shared_ptr时或需要操作该资源时,首先使用weak_ptr来查看该资源是否已经释放,以避免未定义的行为。

要理解其使用场景,首先查看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <vector>
#include <iostream>
#include <array>
#include <memory>

int main(void){
auto spi = std::make_shared<int>(10);
std::weak_ptr<int> wpi(spi);

std::cout << "The value of spi is " << *spi << "\n";
std::cout << "Is wpi expired: " << wpi.expired() << "\n";

//资源已被释放
spi = nullptr;

std::cout << "Is wpi expired: " << wpi.expired() << "\n";

return 0;
}

shared_ptr所指向的资源被释放时,weak_ptrexpired()方法将返回true

但如果是在多线程的应用场景,就有可能出现临界区问题:

  • 线程 T1 通过expired()返回false判定资源还没有被释放,于是决定新建一个shared_ptr
  • 在线程 T1 执行完expired()后,线程 T2 抢占了 T1 运行,T2 中指向同一资源的最后一个shared_ptr被销毁,该资源被释放
  • T1 重新运行,该shared_ptr指向一段未知的堆区域,接下来对该shared_ptr操作结果都是未知的

所以,加锁是必须的:

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
#include <vector>
#include <iostream>
#include <array>
#include <memory>

int main(void){
auto spi = std::make_shared<int>(10);
std::weak_ptr<int> wpi(spi);

std::cout << "The value of spi is " << *spi << "\n";

auto spi2 = wpi.lock();
if(spi2 != nullptr){
std::cout << "The value of spi2 is " << *spi2 << "\n";
}

//资源已被释放
spi = nullptr;
spi2 = nullptr;

auto spi3 = wpi.lock();
if(spi3 != nullptr){
std::cout << "The value of spi3 is " << *spi3 << "\n";
}else{
std::cout << "The resource is not existent!\n";
}

return 0;
}