我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?
当前回答
它们在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示例,其中挂起的异步处理程序不会延长目标对象的生命周期,但如果目标对象被删除,则仍然是安全的。
其他回答
我看到了很多有趣的答案,解释引用计数等,但我错过了一个简单的例子,演示如何使用weak_ptr防止内存泄漏。在第一个例子中,我在循环引用的类中使用shared_ptr。当类超出作用域时,它们不会被销毁。
#include<iostream>
#include<memory>
using namespace std;
class B;
class A
{
public:
shared_ptr<B>bptr;
A() {
cout << "A created" << endl;
}
~A() {
cout << "A destroyed" << endl;
}
};
class B
{
public:
shared_ptr<A>aptr;
B() {
cout << "B created" << endl;
}
~B() {
cout << "B destroyed" << endl;
}
};
int main()
{
{
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
a->bptr = b;
b->aptr = a;
}
// put breakpoint here
}
如果你运行代码片段,你会看到类被创建,但没有被销毁:
A created
B created
现在我们把shared_ptr改成weak_ptr:
class B;
class A
{
public:
weak_ptr<B>bptr;
A() {
cout << "A created" << endl;
}
~A() {
cout << "A destroyed" << endl;
}
};
class B
{
public:
weak_ptr<A>aptr;
B() {
cout << "B created" << endl;
}
~B() {
cout << "B destroyed" << endl;
}
};
int main()
{
{
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
a->bptr = b;
b->aptr = a;
}
// put breakpoint here
}
这一次,当使用weak_ptr时,我们看到了正确的类破坏:
A created
B created
B destroyed
A destroyed
缓存就是一个很好的例子。
对于最近访问的对象,您希望将它们保存在内存中,因此可以保留一个指向它们的强指针。定期扫描缓存,确定最近没有访问哪些对象。你不需要把它们保存在内存中,所以你去掉强指针。
但是,如果该对象正在使用,而其他一些代码持有指向它的强指针,该怎么办?如果缓存删除了指向该对象的唯一指针,就再也找不到它了。因此,缓存保留了一个弱指针,指向它需要找到的对象,如果它们碰巧留在内存中。
这正是弱指针所做的——它允许你在一个对象仍然在附近时定位它,但如果没有其他东西需要它,它就不会保留它。
Std::weak_ptr是解决悬浮指针问题的一个很好的方法。通过使用原始指针,不可能知道所引用的数据是否已被释放。相反,通过让std::shared_ptr管理数据,并将std::weak_ptr提供给数据的用户,用户可以通过调用expired()或lock()来检查数据的有效性。
你不能单独用std::shared_ptr这样做,因为所有std::shared_ptr实例共享数据的所有权,这些数据在std::shared_ptr的所有实例被删除之前没有被删除。下面是一个如何使用lock()检查悬浮指针的例子:
#include <iostream>
#include <memory>
int main()
{
// OLD, problem with dangling pointer
// PROBLEM: ref will point to undefined data!
int* ptr = new int(10);
int* ref = ptr;
delete ptr;
// NEW
// SOLUTION: check expired() or lock() to determine if pointer is valid
// empty definition
std::shared_ptr<int> sptr;
// takes ownership of pointer
sptr.reset(new int);
*sptr = 10;
// get pointer to data without taking ownership
std::weak_ptr<int> weak1 = sptr;
// deletes managed object, acquires new pointer
sptr.reset(new int);
*sptr = 5;
// get pointer to new data without taking ownership
std::weak_ptr<int> weak2 = sptr;
// weak1 is expired!
if(auto tmp = weak1.lock())
std::cout << "weak1 value is " << *tmp << '\n';
else
std::cout << "weak1 is expired\n";
// weak2 points to new data (5)
if(auto tmp = weak2.lock())
std::cout << "weak2 value is " << *tmp << '\n';
else
std::cout << "weak2 is expired\n";
}
输出
weak1 is expired
weak2 value is 5
http://en.cppreference.com/w/cpp/memory/weak_ptr Std::weak_ptr是一个智能指针,它持有对Std::shared_ptr管理的对象的非所有(“弱”)引用。它必须转换为std::shared_ptr才能访问被引用的对象。
Std::weak_ptr建模临时所有权:当一个对象只有在它存在时才需要访问,并且它可能在任何时候被其他人删除时,Std::weak_ptr用于跟踪该对象,并将其转换为Std::shared_ptr以承担临时所有权。如果原始的std::shared_ptr在此时被销毁,对象的生命周期将被延长,直到临时的std::shared_ptr也被销毁。
此外,std::weak_ptr用于打破std::shared_ptr的循环引用。
Here's one example, given to me by @jleahy: Suppose you have a collection of tasks, executed asynchronously, and managed by an std::shared_ptr<Task>. You may want to do something with those tasks periodically, so a timer event may traverse a std::vector<std::weak_ptr<Task>> and give the tasks something to do. However, simultaneously a task may have concurrently decided that it is no longer needed and die. The timer can thus check whether the task is still alive by making a shared pointer from the weak pointer and using that shared pointer, provided it isn't null.
推荐文章
- 什么是“参数依赖查找”(又名ADL,或“Koenig查找”)?
- 公共朋友交换成员函数
- 下面这些短语在c++中是什么意思:0 -,default-和value-initialization?
- 现代c++能让你免费获得性能吗?
- c++11返回值优化或移动?
- 函数标题中的箭头操作符(->)
- c++中“using”关键字背后的逻辑是什么?
- 我如何类型定义一个函数指针与c++ 11使用语法?
- cbegin/cend背后的原因是什么?
- 如何在Eclipse CDT中启用c++ 11/ c++ 0x支持?
- 如何停止c++控制台应用程序从退出立即?
- Lambda捕获作为const引用?
- 为什么在c++ 11中使用非成员begin和end函数?
- 为什么在标准容器中使用std::auto_ptr<>是错误的?
- 空括号的默认构造函数