我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?
当前回答
当我们不想拥有对象时:
Ex:
class A
{
shared_ptr<int> sPtr1;
weak_ptr<int> wPtr1;
}
在上面的类中,wPtr1并不拥有wPtr1所指向的资源。如果资源被删除,那么wPtr1将过期。
避免循环依赖:
shard_ptr<A> <----| shared_ptr<B> <------
^ | ^ |
| | | |
| | | |
| | | |
| | | |
class A | class B |
| | | |
| ------------ |
| |
-------------------------------------
现在如果我们创建类B和A的shared_ptr, both指针的use_count是2。
当shared_ptr超出作用域时,计数仍然保持1,因此A和B对象不会被删除。
class B;
class A
{
shared_ptr<B> sP1; // use weak_ptr instead to avoid CD
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void setShared(shared_ptr<B>& p)
{
sP1 = p;
}
};
class B
{
shared_ptr<A> sP1;
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void setShared(shared_ptr<A>& p)
{
sP1 = p;
}
};
int main()
{
shared_ptr<A> aPtr(new A);
shared_ptr<B> bPtr(new B);
aPtr->setShared(bPtr);
bPtr->setShared(aPtr);
return 0;
}
输出:
A()
B()
正如我们从输出中看到的,A和B指针永远不会被删除,从而导致内存泄漏。
为了避免这样的问题,只需在类A中使用weak_ptr而不是shared_ptr,这更有意义。
其他回答
Weak_ptr也可以很好地检查对象的正确删除——特别是在单元测试中。典型的用例可能是这样的:
std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
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
它们在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
另一个答案,希望更简单。(对谷歌员工)
假设您有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
推荐文章
- 为什么我必须通过this指针访问模板基类成员?
- 为什么非const引用不能绑定到临时对象?
- 什么是std::decay ?什么时候应该使用它?
- 多少是太多的c++ 11 auto关键字?
- 如何从一个地图,而迭代它?
- 到底什么是std::atomic?
- 为什么从字符串常量到'char*'的转换在C中有效,但在c++中无效
- 什么是“*this的右值引用”?
- 函数内的静态constexpr变量有意义吗?
- 'override'关键字是否只是一个被重写的虚拟方法的检查?
- To_string不是std的成员,表示g++ (mingw)
- lambda函数可以被模板化吗?
- 为什么挥发性存在?
- 如何在c++中传递对象给函数?
- 为什么我不能推回一个unique_ptr到一个向量?