我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?
当前回答
Shared_ptr:保存真实对象。
weak_ptr:使用lock连接到真正的所有者,否则返回NULL shared_ptr。
大致来说,weak_ptr角色类似于房屋中介的角色。如果没有中介,要想租到房子,我们可能得在城里随机找房子。中介会确保我们只去那些还能租到的房子。
其他回答
缓存就是一个很好的例子。
对于最近访问的对象,您希望将它们保存在内存中,因此可以保留一个指向它们的强指针。定期扫描缓存,确定最近没有访问哪些对象。你不需要把它们保存在内存中,所以你去掉强指针。
但是,如果该对象正在使用,而其他一些代码持有指向它的强指针,该怎么办?如果缓存删除了指向该对象的唯一指针,就再也找不到它了。因此,缓存保留了一个弱指针,指向它需要找到的对象,如果它们碰巧留在内存中。
这正是弱指针所做的——它允许你在一个对象仍然在附近时定位它,但如果没有其他东西需要它,它就不会保留它。
另一个答案,希望更简单。(对谷歌员工)
假设您有Team和Member对象。
显然,这是一个关系:Team对象将拥有指向其成员的指针。成员也可能有一个指向他们的Team对象的后向指针。
然后你就有了一个依赖循环。如果您使用shared_ptr,当您放弃对对象的引用时,对象将不再被自动释放,因为它们以循环的方式相互引用。这是内存泄漏。
您可以使用weak_ptr来打破这种情况。“所有者”通常使用shared_ptr,而“所有者”使用weak_ptr来访问父节点,并在需要访问父节点时临时将其转换为shared_ptr。
存储一个弱ptr:
weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared
然后在需要的时候使用它
shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
// yes, it may fail if the parent was freed since we stored weak_ptr
} else {
// do stuff
}
// tempParentSharedPtr is released when it goes out of scope
它们在Boost中很有用。当调用异步处理程序时,不能保证目标对象仍然存在。诀窍是使用std::bind或lambda capture将weak_ptr绑定到异步处理程序对象中。
void MyClass::startTimer()
{
std::weak_ptr<MyClass> weak = shared_from_this();
timer_.async_wait( [weak](const boost::system::error_code& ec)
{
auto self = weak.lock();
if (self)
{
self->handleTimeout();
}
else
{
std::cout << "Target object no longer exists!\n";
}
} );
}
这是在Boost中经常看到的self = shared_from_this()习惯用法的变体。Asio示例,其中挂起的异步处理程序不会延长目标对象的生命周期,但如果目标对象被删除,则仍然是安全的。
我看到std::weak_ptr<T>作为std::shared_ptr<T>的句柄:它允许我 获取std::shared_ptr<T>(如果它仍然存在),但是它不会扩展它的 一生。在以下几种情况下,这种观点是有用的:
// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;
// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.
struct Widget {
std::weak_ptr< Texture > texture_handle;
void render() {
if (auto texture = texture_handle.get(); texture) {
// do stuff with texture. Warning: `texture`
// is now extending the lifetime because it
// is a std::shared_ptr< Texture >.
} else {
// gracefully degrade; there's no texture.
}
}
};
另一个重要的场景是打破数据结构中的循环。
// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
std::shared_ptr< Node > next;
std::shared_ptr< Node > prev;
};
// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
std::shared_ptr< Node > parent;
std::shared_ptr< Node > left_child;
std::shared_ptr< Node > right_child;
};
// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
std::shared_ptr< Node > next;
std::weak_ptr< Node > prev;
};
// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
std::weak_ptr< Node > parent;
std::shared_ptr< Node > left_child;
std::shared_ptr< Node > right_child;
};
赫布·萨特有一篇精彩的演讲,解释了语言的最佳使用 特性(在这种情况下是智能指针),以确保默认情况下的泄漏自由 (意思是:所有的东西都是通过建设来实现的;你很难搞砸它 )。这是必看的节目。
受到@offirmo回复的启发,我写了这段代码,然后运行visual studio诊断工具:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
struct Member;
struct Team;
struct Member {
int x = 0;
Member(int xArg) {
x = xArg;
}
shared_ptr<Team> teamPointer;
};
struct Team {
vector<shared_ptr<Member>> members;
};
void foo() {
auto t1 = make_shared<Team>();
for (int i = 0; i < 1000000; i++) {
t1->members.push_back(make_shared<Member>(i));
t1->members.back()->teamPointer = t1;
}
}
int main() {
foo();
while (1);
return 0;
}
当指向团队的成员指针是shared_ptr teamPointer时,在foo()完成后内存就没有空闲了,即它停留在150mb左右。
但是如果在诊断工具中将其更改为weak_ptr teamPointer,您将看到一个峰值,然后内存使用量恢复到大约2MB。
推荐文章
- 为什么我必须通过this指针访问模板基类成员?
- 为什么非const引用不能绑定到临时对象?
- 什么是std::decay ?什么时候应该使用它?
- 多少是太多的c++ 11 auto关键字?
- 如何从一个地图,而迭代它?
- 到底什么是std::atomic?
- 为什么从字符串常量到'char*'的转换在C中有效,但在c++中无效
- 什么是“*this的右值引用”?
- 函数内的静态constexpr变量有意义吗?
- 'override'关键字是否只是一个被重写的虚拟方法的检查?
- To_string不是std的成员,表示g++ (mingw)
- lambda函数可以被模板化吗?
- 为什么挥发性存在?
- 如何在c++中传递对象给函数?
- 为什么我不能推回一个unique_ptr到一个向量?