std::weak_ptr
的任何有用的用途。有人可以告诉我什么时候使用std::weak_ptr
是有用/必要的吗?std::weak_ptr
的任何有用的用途。有人可以告诉我什么时候使用std::weak_ptr
是有用/必要的吗?我将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;
};
Herb Sutter有一个精彩的演讲,解释了最佳语言特性(在这种情况下是智能指针)的使用,以确保默认无泄漏(意思是:通过构造一切都自然而然地嵌入在一起;你几乎不可能搞砸)。这是必看的。
当我们不想拥有对象时:
例如:
class A
{
shared_ptr<int> sPtr1;
weak_ptr<int> wPtr1;
}
shard_ptr<A> <----| shared_ptr<B> <------
^ | ^ |
| | | |
| | | |
| | | |
| | | |
class A | class 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更为明智。
受 @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左右。
共享指针存在一个缺陷:
共享指针无法处理父子循环依赖。也就是说,如果父类使用共享指针使用子类的对象,在同一文件中,如果子类使用父类的对象,共享指针将无法销毁所有对象,即使在循环依赖场景下共享指针根本不会调用析构函数。基本上,共享指针不支持引用计数机制。
我们可以使用弱指针来克服这个缺点。
weak_ptr
如何作为shared_ptr
的替代品处理循环依赖关系而不改变程序逻辑?” :-) - Shelby Moore IIIhttp://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 的循环引用。
std::weak_ptr
打破它们方面做得相当不错: "Back to Basics: Smart Pointers - Rainer Grimm - CppCon 2020" -- https://youtu.be/sQCSX7vmmKY?t=2496 (如果视频从开头开始,则跳至41:36) - Milan