weak_ptr
头文件: "boost/weak_ptr.hpp"
weak_ptr 是 shared_ptr 的观察员。它不会干扰shared_ptr所共享的所有权。当一个被weak_ptr所观察的 shared_ptr 要释放它的资源时,它会把相关的 weak_ptr的指针设为空。这防止了 weak_ptr 持有悬空的指针。你为什么会需要 weak_ptr? 许多情况下,你需要旁观或使用一个共享资源,但不接受所有权,如为了防止递归的依赖关系,你就要旁观一个共享资源而不能拥有所有权,或者为了避免悬空指针。可以从一个weak_ptr构造一个shared_ptr,从而取得对共享资源的访问权。
以下是 weak_ptr的部分定义,列出并简要介绍了最重要的函数。
namespace boost {
template<typename T> class weak_ptr {
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r);
weak_ptr(const weak_ptr& r);
~weak_ptr();
T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}
成员函数
template <typename Y> weak_ptr(const shared_ptr<Y>& r);
这个构造函数从一个shared_ptr创建 weak_ptr ,要求可以从 Y* 隐式转换为 T*. 新的 weak_ptr 被配置为旁观 r所引向的资源。r的引用计数不会有所改变。这意味着r所引向的资源在被删除时不会理睬是否有weak_ptr 引向它。这个构造函数不会抛出异常。
weak_ptr(const weak_ptr& r);
这个复制构造函数让新建的 weak_ptr 旁观weak_ptr r(译注:原文为shared_ptr r,有误)所引向的资源。weak_ptr(译注:原文为shared_ptr,有误)的引用计数保持不变。这个构造函数不会抛出异常。
~weak_ptr();
weak_ptr 的析构函数,和构造函数一样,它不改变引用计数。如果需要,析构函数会把 *this 与共享资源脱离开。这个析构函数不会抛出异常。
bool expired() const;
如果所观察的资源已经"过期",即资源已被释放,则返回 True 。如果保存的指针为非空,expired 返回 false. 这个函数不会抛出异常。
shared_ptr<T> lock() const
返回一个引向weak_ptr所观察的资源的 shared_ptr ,如果可以的话。如果没有这样指针(即 weak_ptr 引向的是空指针),shared_ptr 也将引向空指针。否则,shared_ptr所引向的资源的引用计数将正常地递增。这个函数不会抛出异常。
用法
我们从一个示范weak_ptr的基本用法的例子开始,尤其要看看它是如何不影响引用计数的。这个例子里也包含了 shared_ptr,因为 weak_ptr 总是需要和 shared_ptr一起使用的。使用 weak_ptr 要包含头文件 "boost/weak_ptr.hpp".
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
#include <iostream>
#include <cassert>
class A {};
int main() {
boost::weak_ptr<A> w;
assert(w.expired());
{
boost::shared_ptr<A> p(new A());
assert(p.use_count()==1);
w=p;
assert(p.use_count()==w.use_count());
assert(p.use_count()==1);
// 从weak_ptr创建shared_ptr
boost::shared_ptr<A> p2(w);
assert(p2==p);
}
assert(w.expired());
boost::shared_ptr<A> p3=w.lock();
assert(!p3);
}
weak_ptr w 被缺省构造,意味着它初始时不旁观任何资源。要检测一个 weak_ptr 是否在旁观一个活的对象,你可以使用函数 expired. 要开始旁观,weak_ptr 必须要被赋值一个 shared_ptr. 本例中,shared_ptr p 被赋值给 weak_ptr w, 这等于说p 和 w 的引用计数应该是相同的。然后,再从weak_ptr构造一个shared_ptr,这是一种从weak_ptr那里获得对共享资源的访问权的方法。如果在构造shared_ptr时,weak_ptr 已经过期了,将从shared_ptr的构造函数里抛出一个 boost::bad_weak_ptr 类型的异常。再继续,当 shared_ptr p 离开作用域,w 就变成过期的了。当调用它的成员函数 lock 来获得一个shared_ptr时,这是另一种获得对共享资源访问权的方法,将返回一个空的 shared_ptr 。注意,从这个程序的开始到结束,weak_ptr 都没有影响到共享对象的引用计数的值。
与其它智能指针不同的是,weak_ptr 不对它所观察的指针提供重载的 operator* 和 operator->. 原因是对weak_ptr所观察的资源的任何操作都必须是明显的,这样才安全;由于不会影响它们所观察的共享资源的引用计数器,所以真的很容易就会不小心访问到一个无效的指针。这就是为什么你必须要传送 weak_ptr 给 shared_ptr的构造函数,或者通过调用weak_ptr::lock来获得一个 shared_ptr 。这两种方法都会使引用计数增加,这样在 shared_ptr 从 weak_ptr创建以后,它可以保证共享资源的生存,确保在我们要使用它的时候它不会被释放掉。
常见问题
由于在智能指针中保存的是指针的值而不是它们所指向的指针的值,因此在标准库容器中使用智能指针有一个常见的问题,就是如何在算法中使用智能指针;算法通常需要访问实际对象的值,而不是它们的地址。例如,你如何调用 std::sort 并正确地排序?实际上,这个问题与在容器中保存并操作普通指针是几乎一样的,但事实很容易被忽略(可能是由于我们总是避免在容器中保存裸指针)。当然我们不能直接比较两个智能指针的值,但也很容易解决。只要用一个解引用智能指针的谓词就可以了,所以我们将创建一个可重用的谓词,使得可以在标准库的算法里使用引向智能指针的迭代器,这里我们选用的智能指针是 weak_ptr。
#include <functional>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
template <typename Func, typename T>
struct weak_ptr_unary_t :
public std::unary_function<boost::weak_ptr<T>,bool> {
T t_;
Func func_;
weak_ptr_unary_t(const Func& func,const T& t)
: t_(t),func_(func) {}
bool operator()(boost::weak_ptr<T> arg) const {
boost::shared_ptr<T> sp=arg.lock();
if (!sp) {
return false;
}
return func_(*sp,t_);
}
};
template <typename Func, typename T> weak_ptr_unary_t<Func,T>
weak_ptr_unary(const Func& func, const T& value) {
return weak_ptr_unary_t<Func,T>(func,value);
}
weak_ptr_unary_t 函数对象对要调用的函数以及函数所用的参数类型进行了参数化。把要调用的函数保存在函数对象中使用使得这个函数对象很容易使用,很快我们就能看到这一点。为了使这个谓词兼容于标准库的适配器,weak_ptr_unary_t 要从 std::unary_function派生,后者保证了所有需要的 typedefs 都能提供(这些要求是为了让标准库的适配器可以这些函数对象一起工作)。实际的工作在调用操作符函数中完成,从weak_ptr创建一个 shared_ptr 。必须要确保在函数调用时资源是可用的。然后才可以调用指定的函数(或函数对象),传入本次调用的参数(要解引用以获得真正的资源) 和在对象中保存的值,这个值是在构造weak_ptr_unary_t时给定的。这个简单的函数对象现在可以用于任意可用的算法了。为方便起见,我们还定义了一个助手函数,weak_ptr_unary, 它可以推出参数的类型并返回一个适当的函数对象。我们来看看如何使用它。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
int main() {
using std::string;
using std::vector;
using boost::shared_ptr;
using boost::weak_ptr;
vector<weak_ptr<string> > vec;
shared_ptr<string> sp1(
new string("An example"));
shared_ptr<string> sp2(
new string("of using"));
shared_ptr<string> sp3(
new string("smart pointers and predicates"));
vec.push_back(weak_ptr<string>(sp1));
vec.push_back(weak_ptr<string>(sp2));
vec.push_back(weak_ptr<string>(sp3));
vector<weak_ptr<string> >::iterator
it=std::find_if(vec.begin(),vec.end(),
weak_ptr_unary(std::equal_to<string>(),string("of using")));
if (it!=vec.end()) {
shared_ptr<string> sp(*++it);
std::cout << *sp << '\n';
}
}
本例中,创建了一个包含weak_ptr的 vector。最有趣的一行代码(是的,它有点长)就是我们为使用find_if算法而创建weak_ptr_unary_t的那行。
vector<weak_ptr<string> >::iterator it=std::find_if(
vec.begin(),
vec.end(),
weak_ptr_unary(
std::equal_to<string>(),string("of using")));
通过把另一个函数对象,std::equal_to, 和一个用于匹配的string一起传给助手函数weak_ptr_unary,创建了一个新的函数对象。由于 weak_ptr_unary_t 完全兼容于各种适配器(由于它是从std::unary_function派生而来的),我们可以再从它组合出各种各样的函数对象。例如,我们也可以查找第一个不匹配"of using"的串:
vector<weak_ptr<string> >::iterator it=std::find_if(
vec.begin(),
vec.end(),
std::not1(
weak_ptr_unary(
std::equal_to<string>(),string("of using"))));
Boost智能指针是专门为了与标准库配合工作而设计的。我们可以创建有用的组件来帮助我们可以更简单地使用这些强大的智能指针。象 weak_ptr_unary 这样的工具并不是经常要用到的;有一个库提供了比weak_ptr_unary更好用的泛型绑定器[15]。弄懂这些智能指针的语义,可以让我们更清楚地使用它们。
两种从weak_ptr创建shared_ptr的惯用法
如你所见,如果你有一个旁观某种资源的 weak_ptr ,你最终还是会想要访问这个资源。为此,weak_ptr 必须被转换为 shared_ptr, 因为 weak_ptr 是不允许访问资源的。有两种方法可以从weak_ptr创建shared_ptr:把 weak_ptr 传递给 shared_ptr 的构造函数,或者调用 weak_ptr 的成员函数lock, 它返回 shared_ptr. 选择哪一个取决于你认为一个空的 weak_ptr 是错误的抑或不是。shared_ptr 构造函数在接受一个空的 weak_ptr 参数时会抛出一个 bad_weak_ptr 类型的异常。因此应该在你认为空的 weak_ptr 是一种错误时使用它。如果使用 weak_ptr 的函数 lock, 它会在weak_ptr为空时返回一个空的 shared_ptr。这在你想测试一个资源是否有效时是正确的,一个空的 weak_ptr 是预期中的。此外,如果使用 lock, 那么使用资源的正确方法应该是初始化并同时测试它,如下:
#include <iostream>
#include <string>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
int main() {
boost::shared_ptr<std::string>
sp(new std::string("Some resource"));
boost::weak_ptr<std::string> wp(sp);
// ...
if (boost::shared_ptr<std::string> p=wp.lock())
std::cout << "Got it: " << *p << '\n';
else
std::cout << "Nah, the shared_ptr is empty\n";
}
如你所见,shared_ptr p 被weak_ptr wp 的lock函数的结果初始化。然后 p 被测试,只有当它非空时资源才能被访问。由于 shared_ptr 仅在这个作用域中有效,所以在这个作用域之外不会有机会让你不小心用到它。另一种情形是当 weak_ptr 逻辑上必须非空的时候。那种情形下,不需要测试 shared_ptr 是否为空,因为 shared_ptr 的构造函数会在接受一个空weak_ptr时抛出异常,如下:
#include <iostream>
#include <string>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
void access_the_resource(boost::weak_ptr<std::string> wp) {
boost::shared_ptr<std::string> sp(wp);
std::cout << *sp << '\n';
}
int main() {
boost::shared_ptr<std::string>
sp(new std::string("Some resource"));
boost::weak_ptr<std::string> wp(sp);
// ...
access_the_resource(wp);
}
在这个例子中,函数 access_the_resource 从一个weak_ptr构造 shared_ptr sp 。这时不需要测试 shared_ptr 是否为空,因为如果 weak_ptr 为空,将会抛出一个 bad_weak_ptr 类型的异常,因此函数会立即结束;错误会在适当的时候被捕获和处理。这样做比显式地测试 shared_ptr 是否为空然后返回要更好。这就是从weak_ptr获得shared_ptr的两种方法。
总结
weak_ptr 是Boost智能指针拼图的最后一块。weak_ptr 概念是shared_ptr的一个重要伙伴。它允许我们打破递归的依赖关系。它还处理了关于悬空指针的一个常见问题。在共享一个资源时,它常用于那些不参与生存期管理的资源用户。这种情况不能使用裸指针,因为在最后一个 shared_ptr 被销毁时,它会释放掉共享的资源。如果使用裸指针来引用资源,将无法知道资源是否仍然存在。如果资源已经不存在,访问它将会引起灾难。通过使用 weak_ptr, 关于共享资源已被销毁的信息会传播给所有旁观的 weak_ptrs,这意味着不会发生无意间访问到无效指针的情形。这就象是观察员模式(Observer pattern)的一个特例;当资源被销毁,所有表示对此感兴趣的都会被通知到。
对于以下情形使用 weak_ptr :
-
要打破递归的依赖关系
-
使用一个共享的资源而不需要共享所有权
-
避免悬空的指针
分享到:
相关推荐
在 C++ 中,有四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,每种智能指针都有其特点和使用场景。 一、auto_ptr auto_ptr 是 C++98 中引入的智能指针,它可以自动释放动态分配的内存。但是,auto_...
C++ 智能指针(shared_ptr/weak_ptr)源码 源码位置:gcc-6.1.0\gcc-6.1.0\libstdc++-v3\include\tr1 这里只单列shared_ptr.h文件用于分析
Boost库提供了一组智能指针类型,包括scoped_ptr、shared_ptr和weak_ptr,它们各自有特定的用途和限制。 ### 1. scoped_ptr `boost::scoped_ptr` 是一种非拷贝性的智能指针,它的设计目标是保证对象在其作用域内...
本文主要讨论了三种智能指针:`shared_ptr`、`unique_ptr`和`weak_ptr`。 首先,`shared_ptr`是引用计数型智能指针,它维护了一个内部的引用计数,用于跟踪有多少个`shared_ptr`实例指向同一对象。当创建一个新的`...
其中,`weak_ptr`是为了解决`shared_ptr`中可能出现的循环引用问题而引入的一种特殊类型的智能指针。本文将深入探讨`weak_ptr`的概念、用途及其使用方法。 1. 为什么需要`weak_ptr`? `shared_ptr`是C++11中引入的...
`shared_ptr`和`weak_ptr`是两种常见的智能指针类型,分别代表了强引用和弱引用的概念。 `shared_ptr`是C++11引入的一种智能指针,它管理一个动态分配的对象,并在不再有引用指向该对象时自动删除它。`shared_ptr`...
Boost库提供了几种智能指针类型,其中`shared_ptr`和`weak_ptr`是两个关键的成员。在这个名为"Boost智能指针示例源码"的压缩包中,我们有机会深入理解并学习如何在实际项目中使用这些智能指针。 首先,`shared_ptr`...
4. **智能指针**:讲解`boost::shared_ptr`、`boost::weak_ptr`等智能指针,以及它们在资源管理中的作用,避免内存泄漏和悬空指针问题。 5. **函数对象和绑定**:介绍`boost::function`和`boost::bind`,这些工具...
`shared_ptr`还支持一些高级特性,如`weak_ptr`,它可以监视一个`shared_ptr`的引用计数,但不会增加它。当`shared_ptr`的引用计数降为0时,即使还有`weak_ptr`存在,对象也会被释放。这在避免循环引用和处理依赖...
5. 弱指针`std::weak_ptr`:与`std::shared_ptr`配合使用的弱指针不增加对象的引用计数,主要用于解决循环引用问题。当弱指针试图访问对象时,需要先检查对象是否仍然有效。 ```cpp std::weak_ptr<int> weak_ptr...
在C++中,标准库提供了几种智能指针类型,如`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`。这些智能指针在析构时会自动调用所指对象的析构函数,从而释放内存。 2. **自定义智能指针`count_ptr`**: ...
在智能指针家族中,`shared_ptr`和`weak_ptr`是TR1(Technical Report 1)的一部分,也被称为Boost库的一部分,并被整合到了C++11标准中。 ### shared_ptr `shared_ptr`是一种引用计数智能指针,当一个`shared_ptr...
为了更好地解决这些问题,Boost库提供了一系列智能指针,其中最常用的是`shared_ptr`和`weak_ptr`。本文将重点介绍`weak_ptr`的基本用法及其与`shared_ptr`的配合使用。 #### `shared_ptr`与`weak_ptr`简介 - **`...
1. **Smart Pointers**:Boost库提供了智能指针(如shared_ptr、unique_ptr、weak_ptr),这些指针能帮助开发者更安全地管理内存,避免内存泄漏问题。 2. **Multithreading**:Boost.Thread库提供了线程管理和同步...
`unique_ptr`的删除器通常在构造函数中指定,而`shared_ptr`的删除器可以通过弱指针(`weak_ptr`)共享,以确保即使所有`shared_ptr`都失效后,仍能正确释放资源。 3. **构造函数和赋值操作符**:为了创建和操作...
重点掌握 unique_ptr 原理,shared_ptr 原理(引用计数管理方法),weak_ptr 与 shared_ptr的区别:weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命...
在C++标准库中,`std::shared_ptr`(在早期版本中可能使用`boost::shared_ptr`)是一种智能指针,它通过引用计数来管理动态分配的对象生命周期。`std::shared_ptr`的设计目的是自动管理对象的内存,当最后一个指向该...