前面两篇文章已经介绍了 std::weak_ptr
,相比之下,std::weak_ptr
没有std::shared_ptr
和std::shared_ptr
好理解,今天通过几个例子看看std::weak_ptr
的用法。
std::weak_ptr
的提出主要是用来解决 std::shared_ptr
带来的循环引用问题。
与 std::shared_ptr
不同,std::weak_ptr
并不拥有对象,也就是说,它的存在不会增加对象的引用计数。这意味着 std::weak_ptr
指向一个对象时,它不会阻止该对象被销毁。std::weak_ptr
适用于跟踪由 std::shared_ptr
管理的对象,而不需要延长该对象的生命周期。
std::weak_ptr
的主要用法包括:
-
解决循环引用:当两个对象互相持有对方的
std::shared_ptr
时,会形成循环引用,导致内存泄漏。通过将其中一个对象对另一个对象的引用改为std::weak_ptr
,可以打破循环引用,使得对象能够被正确释放。 -
观察者模式:在观察者模式中,观察者(Observer)通常需要跟踪可观察对象(Observable),但不拥有它。使用
std::weak_ptr
可以实现这一点,观察者可以通过std::weak_ptr
来检查可观察对象是否仍然存在,并在需要时与之交互。 -
缓存实现:当实现缓存时,你可能不希望缓存的存在延长对象的生命周期。
std::weak_ptr
可以用来跟踪对象,如果对象仍然存在,就可以通过将std::weak_ptr
提升为std::shared_ptr
来使用它,否则可以认为对象已经被销毁或需要重新创建。
使用示例
以下是使用 std::weak_ptr
的一些示例代码,展示了上述用法。
解决循环引用
前面已经给出了一个简单的例子。这里不再重复。
观察者模式
#include <iostream>
#include <memory>
#include <vector>
class Observable; // 前向声明
class Observer {
public:
virtual void update() = 0; // 纯虚函数,供派生类实现
virtual ~Observer() = default;
};
class Observable {
public:
void addObserver(const std::weak_ptr<Observer>& observer) {
observers.push_back(observer);
}
void notifyObservers() {
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto observer = it->lock()) {
observer->update();
++it;
} else {
// 如果观察者已经被销毁,从列表中移除
it = observers.erase(it);
}
}
}
private:
std::vector<std::weak_ptr<Observer>> observers;
};
class ConcreteObserver : public Observer, public std::enable_shared_from_this<ConcreteObserver> {
public:
void update() override {
std::cout << "Observer updated.\n";
}
};
int main() {
auto observable = std::make_shared<Observable>();
auto observer1 = std::make_shared<ConcreteObserver>();
auto observer2 = std::make_shared<ConcreteObserver>();
observable->addObserver(observer1);
observable->addObserver(observer2);
observable->notifyObservers();
return 0;
}
输出:
Observer updated.
Observer updated.
在这个例子中,Observable
类拥有一个 Observer
对象列表,Observer
通过 std::weak_ptr
被添加到列表中。这样,即使 Observer
对象被销毁,它也不会影响到 Observable
对象的行为。
缓存实现
#include <iostream>
#include <memory>
#include <map>
class Resource;
std::map<int, std::weak_ptr<Resource>> cache;
class Resource {
public:
Resource(int id) : resourceId(id) {
std::cout << "Resource " << id << " created.\n";
}
~Resource() {
std::cout << "Resource " << resourceId << " destroyed.\n";
}
void use() {
std::cout << "Using resource " << resourceId << ".\n";
}
private:
int resourceId;
};
std::shared_ptr<Resource> getResource(int id) {
if (auto found = cache[id].lock()) {
return found;
} else {
auto newResource = std::make_shared<Resource>(id);
cache[id] = newResource;
return newResource;
}
}
int main() {
auto res1 = getResource(1); // 创建资源
res1->use(); // 使用资源
res1.reset(); // 显式释放资源
auto res2 = getResource(1); // 由于资源已经释放,这里会创建一个新资源
return 0;
}
输出:
Resource 1 created.
Using resource 1.
Resource 1 destroyed.
Resource 1 created.
Resource 1 destroyed.
在这个缓存实现示例中,当尝试获取一个资源时,会首先检查该资源是否已存在于缓存中。
如果 std::weak_ptr
能够被提升为 std::shared_ptr
,说明资源仍然存在;否则,会创建一个新的资源并将其加入到缓存中。
这样做既避免了不必要的资源重复创建,又确保了资源可以在不再被需要时正确地被销毁。
总结
std::weak_ptr
提供了一种机制,来观察和跟踪 std::shared_ptr
所管理的对象,而不延长其生命周期。这在解决循环引用、实现观察者模式、以及在特定场景下管理缓存时非常有用。
通过 std::weak_ptr
,开发者可以更灵活地管理内存,避免潜在的内存泄漏问题。