我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?
当前回答
在使用指针时,重要的是要了解可用的不同类型的指针,以及何时使用每种指针是有意义的。指针分为以下两类:
原始指针: 原始指针[即SomeClass* ptrToSomeClass = new SomeClass();] 智能指针: 唯一指针[即std::unique_ptr<SomeClass> uniquePtrToSomeClass (new SomeClass());] 共享指针[即std::shared_ptr<SomeClass> sharedPtrToSomeClass (new SomeClass());] 弱指针[即std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr (weakOrSharedPtr);]
Raw pointers (sometimes referred to as "legacy pointers", or "C pointers") provide 'bare-bones' pointer behavior and are a common source of bugs and memory leaks. Raw pointers provide no means for keeping track of ownership of the resource and developers must call 'delete' manually to ensure they are not creating a memory leak. This becomes difficult if the resource is shared as it can be challenging to know whether any objects are still pointing to the resource. For these reasons, raw pointers should generally be avoided and only used in performance-critical sections of the code with limited scope.
Unique pointers are a basic smart pointer that 'owns' the underlying raw pointer to the resource and is responsible for calling delete and freeing the allocated memory once the object that 'owns' the unique pointer goes out of scope. The name 'unique' refers to the fact that only one object may 'own' the unique pointer at a given point in time. Ownership may be transferred to another object via the move command, but a unique pointer can never be copied or shared. For these reasons, unique pointers are a good alternative to raw pointers in the case that only one object needs the pointer at a given time, and this alleviates the developer from the need to free memory at the end of the owning object's lifecycle.
Shared pointers are another type of smart pointer that are similar to unique pointers, but allow for many objects to have ownership over the shared pointer. Like unique pointer, shared pointers are responsible for freeing the allocated memory once all objects are done pointing to the resource. It accomplishes this with a technique called reference counting. Each time a new object takes ownership of the shared pointer the reference count is incremented by one. Similarly, when an object goes out of scope or stops pointing to the resource, the reference count is decremented by one. When the reference count reaches zero, the allocated memory is freed. For these reasons, shared pointers are a very powerful type of smart pointer that should be used anytime multiple objects need to point to the same resource.
最后,弱指针是另一种类型的智能指针,它们不是直接指向资源,而是指向另一个指针(弱指针或共享指针)。弱指针不能直接访问对象,但它们可以判断对象是否仍然存在或是否已经过期。弱指针可以临时转换为共享指针以访问所指向的对象(前提是它仍然存在)。为了说明这一点,考虑下面的例子:
您很忙,会议A和会议B有重叠 你决定去开会A,而你的同事去开会B 你告诉你的同事,如果A会议结束后B会议还在进行,你也会加入 可能会出现以下两种情况: 会议A结束了,会议B还在进行,所以你加入了 会议A已经结束,会议B也已经结束,您不能加入
在本例中,您有一个指向会议B的弱指针。您不是会议B的“所有者”,因此会议B可以在没有您的情况下结束,并且您不知道它是否结束,除非您检查。如果它还没有结束,你可以加入和参与,否则,你不能。这与拥有一个指向会议B的共享指针不同,因为您将同时成为会议a和会议B的“所有者”(同时参与这两个会议)。
The example illustrates how a weak pointer works and is useful when an object needs to be an outside observer, but does not want the responsibility of sharing ownership. This is particularly useful in the scenario that two objects need to point to each other (a.k.a. a circular reference). With shared pointers, neither object can be released because they are still 'strongly' pointed to by the other object. When one of the pointers is a weak pointer, the object holding the weak pointer can still access the other object when needed, provided it still exists.
其他回答
在使用指针时,重要的是要了解可用的不同类型的指针,以及何时使用每种指针是有意义的。指针分为以下两类:
原始指针: 原始指针[即SomeClass* ptrToSomeClass = new SomeClass();] 智能指针: 唯一指针[即std::unique_ptr<SomeClass> uniquePtrToSomeClass (new SomeClass());] 共享指针[即std::shared_ptr<SomeClass> sharedPtrToSomeClass (new SomeClass());] 弱指针[即std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr (weakOrSharedPtr);]
Raw pointers (sometimes referred to as "legacy pointers", or "C pointers") provide 'bare-bones' pointer behavior and are a common source of bugs and memory leaks. Raw pointers provide no means for keeping track of ownership of the resource and developers must call 'delete' manually to ensure they are not creating a memory leak. This becomes difficult if the resource is shared as it can be challenging to know whether any objects are still pointing to the resource. For these reasons, raw pointers should generally be avoided and only used in performance-critical sections of the code with limited scope.
Unique pointers are a basic smart pointer that 'owns' the underlying raw pointer to the resource and is responsible for calling delete and freeing the allocated memory once the object that 'owns' the unique pointer goes out of scope. The name 'unique' refers to the fact that only one object may 'own' the unique pointer at a given point in time. Ownership may be transferred to another object via the move command, but a unique pointer can never be copied or shared. For these reasons, unique pointers are a good alternative to raw pointers in the case that only one object needs the pointer at a given time, and this alleviates the developer from the need to free memory at the end of the owning object's lifecycle.
Shared pointers are another type of smart pointer that are similar to unique pointers, but allow for many objects to have ownership over the shared pointer. Like unique pointer, shared pointers are responsible for freeing the allocated memory once all objects are done pointing to the resource. It accomplishes this with a technique called reference counting. Each time a new object takes ownership of the shared pointer the reference count is incremented by one. Similarly, when an object goes out of scope or stops pointing to the resource, the reference count is decremented by one. When the reference count reaches zero, the allocated memory is freed. For these reasons, shared pointers are a very powerful type of smart pointer that should be used anytime multiple objects need to point to the same resource.
最后,弱指针是另一种类型的智能指针,它们不是直接指向资源,而是指向另一个指针(弱指针或共享指针)。弱指针不能直接访问对象,但它们可以判断对象是否仍然存在或是否已经过期。弱指针可以临时转换为共享指针以访问所指向的对象(前提是它仍然存在)。为了说明这一点,考虑下面的例子:
您很忙,会议A和会议B有重叠 你决定去开会A,而你的同事去开会B 你告诉你的同事,如果A会议结束后B会议还在进行,你也会加入 可能会出现以下两种情况: 会议A结束了,会议B还在进行,所以你加入了 会议A已经结束,会议B也已经结束,您不能加入
在本例中,您有一个指向会议B的弱指针。您不是会议B的“所有者”,因此会议B可以在没有您的情况下结束,并且您不知道它是否结束,除非您检查。如果它还没有结束,你可以加入和参与,否则,你不能。这与拥有一个指向会议B的共享指针不同,因为您将同时成为会议a和会议B的“所有者”(同时参与这两个会议)。
The example illustrates how a weak pointer works and is useful when an object needs to be an outside observer, but does not want the responsibility of sharing ownership. This is particularly useful in the scenario that two objects need to point to each other (a.k.a. a circular reference). With shared pointers, neither object can be released because they are still 'strongly' pointed to by the other object. When one of the pointers is a weak pointer, the object holding the weak pointer can still access the other object when needed, provided it still exists.
缓存就是一个很好的例子。
对于最近访问的对象,您希望将它们保存在内存中,因此可以保留一个指向它们的强指针。定期扫描缓存,确定最近没有访问哪些对象。你不需要把它们保存在内存中,所以你去掉强指针。
但是,如果该对象正在使用,而其他一些代码持有指向它的强指针,该怎么办?如果缓存删除了指向该对象的唯一指针,就再也找不到它了。因此,缓存保留了一个弱指针,指向它需要找到的对象,如果它们碰巧留在内存中。
这正是弱指针所做的——它允许你在一个对象仍然在附近时定位它,但如果没有其他东西需要它,它就不会保留它。
它们在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;
};
赫布·萨特有一篇精彩的演讲,解释了语言的最佳使用 特性(在这种情况下是智能指针),以确保默认情况下的泄漏自由 (意思是:所有的东西都是通过建设来实现的;你很难搞砸它 )。这是必看的节目。
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
推荐文章
- 什么是奇怪的重复模板模式(CRTP)?
- 如何自动转换强类型枚举为int?
- 在一个类中使用具有成员函数的泛型std::function对象
- 当启用c++ 11时,std::vector性能回归
- 什么时候使用哪种指针?
- 使用c++ 11的“auto”可以提高性能吗?
- 使用c++ 11的基于范围的正确方法是什么?
- 为什么我必须通过this指针访问模板基类成员?
- 为什么非const引用不能绑定到临时对象?
- 什么是std::decay ?什么时候应该使用它?
- 多少是太多的c++ 11 auto关键字?
- 如何从一个地图,而迭代它?
- 到底什么是std::atomic?
- 为什么从字符串常量到'char*'的转换在C中有效,但在c++中无效
- 什么是“*this的右值引用”?