我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?
当前回答
Shared_ptr:保存真实对象。
weak_ptr:使用lock连接到真正的所有者,否则返回NULL shared_ptr。
大致来说,weak_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。
共享指针有一个缺点: Shared_pointer不能处理父子周期依赖关系。如果父类使用父类的对象使用共享指针,则表示在同一文件中,如果子类使用父类的对象。共享指针将无法析构所有对象,甚至在循环依赖场景中共享指针根本不调用析构函数。基本上共享指针不支持引用计数机制。
我们可以使用weak_pointer来克服这个缺点。
我看到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;
};
赫布·萨特有一篇精彩的演讲,解释了语言的最佳使用 特性(在这种情况下是智能指针),以确保默认情况下的泄漏自由 (意思是:所有的东西都是通过建设来实现的;你很难搞砸它 )。这是必看的节目。
当我们不想拥有对象时:
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,这更有意义。
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
推荐文章
- Shared_ptr转换为数组:应该使用它吗?
- c++ 11中的递归lambda函数
- c++ auto关键字。为什么它是魔法?
- Std::auto_ptr改为Std::unique_ptr
- 什么是“参数依赖查找”(又名ADL,或“Koenig查找”)?
- 公共朋友交换成员函数
- 下面这些短语在c++中是什么意思:0 -,default-和value-initialization?
- 现代c++能让你免费获得性能吗?
- c++11返回值优化或移动?
- 函数标题中的箭头操作符(->)
- c++中“using”关键字背后的逻辑是什么?
- 我如何类型定义一个函数指针与c++ 11使用语法?
- cbegin/cend背后的原因是什么?
- 如何在Eclipse CDT中启用c++ 11/ c++ 0x支持?
- 如何停止c++控制台应用程序从退出立即?