C++11智能指针之weak_ptr详解
简介
C++11添加了4种智能指针:unique_ptr、shared_ptr、weak_ptr、auto_ptr。其中weak_ptr是一种弱引用类型的指针,它不对所指对象进行引用计数,可以防止 shared_ptr 的循环引用问题。
特点
- weak_ptr 所指向的对象可能已经被删除了,因此在使用 weak_ptr 之前需要进行判空操作;
- weak_ptr 不能直接访问其所指向的对象,需要将其转化为 shared_ptr 才能进行访问;
- 不要将一个 new 出来的对象直接赋值给 weak_ptr,这样会造成强制删除动态分配对象,从而导致行为未定义。
使用场景
- 防止 shared_ptr 循环引用问题
当对象之间存在循环引用的时候,shared_ptr 会在引用计数清零时,删除对象,但是循环引用导致引用计数无法减为零,致使引起内存泄露。此时可以用 weak_ptr 来解决这个问题。
例如:
struct Node {
std::shared_ptr<Node> next;
};
std::shared_ptr<Node> n1(new Node);
std::shared_ptr<Node> n2(new Node);
n1->next = n2; // n1 引用计数为 1,n2 引用计数为 2
n2->next = n1; // n1 引用计数为 2,n2 引用计数为 2
上面的例子会导致循环引用,引用计数无法清零,使用 weak_ptr 可以解决这个问题。
struct Node {
std::weak_ptr<Node> next; // 使用 weak_ptr 替代 shared_ptr
};
std::shared_ptr<Node> n1(new Node);
std::shared_ptr<Node> n2(new Node);
n1->next = n2;
n2->next = n1;
- 用于缓存对象
当对象不再被引用时,可以选择删除它从而释放资源。然而,在某些情况下,被引用的对象可能在之后还需要用到。此时,可以将这些对象保存在缓存中。缓存不会持有对象的所有权,而只是提供了对象的一种访问方式。使用 weak_ptr 可以实现这个功能。
例如:
class Cache {
public:
void add(const std::string& name, std::shared_ptr<Object> obj) {
cache[name] = obj;
}
std::weak_ptr<Object> get(const std::string& name) {
auto iter = cache.find(name);
if (iter != cache.end()) {
return iter->second;
}
return std::weak_ptr<Object>();
}
private:
std::map<std::string, std::shared_ptr<Object>> cache;
};
在上面的例子中,Cache 对象只会保存 Object 对象的弱引用,这样就不会阻止 Object 对象的销毁,同时可以在需要时访问 Object 对象。
示例
示例一:循环引用时使用 weak_ptr
#include <memory>
#include <iostream>
class B;
class A {
public:
std::weak_ptr<B> pb_;
~A() {
std::cout << "A is destroyed." << std::endl;
}
};
class B {
public:
std::shared_ptr<A> pa_;
~B() {
std::cout << "B is destroyed." << std::endl;
}
};
int main() {
std::shared_ptr<A> a(new A);
std::shared_ptr<B> b(new B);
a->pb_ = b;
b->pa_ = a;
return 0;
}
上面的代码存在循环引用,导致 a 和 b 无法被释放。修改如下:
class B;
class A {
public:
std::weak_ptr<B> pb_;
~A() {
std::cout << "A is destroyed." << std::endl;
}
};
class B {
public:
std::weak_ptr<A> pa_;
~B() {
std::cout << "B is destroyed." << std::endl;
}
};
int main() {
std::shared_ptr<A> a(new A);
std::shared_ptr<B> b(new B);
a->pb_ = std::weak_ptr<B>(b);
b->pa_ = std::weak_ptr<A>(a);
return 0;
}
将 A 类中的 pb_ 指针改为 weak_ptr 类型,B 类中的 pa_ 指针也改为 weak_ptr 类型,就可以正确释放对象了。
示例二:用于缓存对象
#include <iostream>
#include <memory>
#include <string>
#include <map>
class Object {
public:
Object(const std::string& name) : name_(name) {}
~Object() {
std::cout << name_ << " is destroyed." << std::endl;
}
private:
std::string name_;
};
class Cache {
public:
void add(const std::string& name, std::shared_ptr<Object> obj) {
cache[name] = obj;
}
std::weak_ptr<Object> get(const std::string& name) {
auto iter = cache.find(name);
if (iter != cache.end()) {
return iter->second;
}
return std::weak_ptr<Object>();
}
private:
std::map<std::string, std::shared_ptr<Object>> cache;
};
int main() {
std::shared_ptr<Object> o1 = std::make_shared<Object>("object1");
std::shared_ptr<Object> o2 = std::make_shared<Object>("object2");
Cache cache;
cache.add("obj1", o1);
std::weak_ptr<Object> wo1 = cache.get("obj1");
if (!wo1.expired()) {
std::shared_ptr<Object> so1 = wo1.lock();
std::cout << "Name: " << "obj1" << ", Object: " << so1 << std::endl;
}
cache.add("obj2", o2);
std::weak_ptr<Object> wo2 = cache.get("obj2");
if (!wo2.expired()) {
std::shared_ptr<Object> so2 = wo2.lock();
std::cout << "Name: " << "obj2" << ", Object: " << so2 << std::endl;
}
std::weak_ptr<Object> wo3 = cache.get("obj3");
if (wo3.expired()) {
std::cout << "Object is expired." << std::endl;
}
return 0;
}
在上面的代码中,Cache 对象只会保存 Object 对象的弱引用,这样就不会阻止 Object 对象的销毁,同时可以在需要时访问 Object 对象。这样就可以做到缓存而不造成内存泄漏。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++11智能指针之weak_ptr详解 - Python技术站