std::weak_ptr
std::weak_ptr
是一种不拥有对象所有权的智能指针,用于观察但不影响对象的生命周期。主要用于解决 shared_ptr
之间的循环引用问题。
主要特性:
shared_ptr
生成:通过 std::weak_ptr
可以访问 shared_ptr
管理的对象。在存在双向关联(如父子关系)时,使用多个 shared_ptr
可能导致循环引用,导致内存泄漏。此时,可以使用 weak_ptr
来打破循环。
#include
#include
#include
#include
// -------------------- 抽象接口部分 --------------------
// 观察者接口:所有 App 都要实现这个接口
class Observer {
public:
virtual void update(float temp) = 0; // 被通知时调用
virtual ~Observer() {
std::cout << "Observer destroyed" << std::endl;
}
};
// 主题(被观察者)接口:天气预报系统实现它
class Subject {
public:
virtual void attach(std::shared_ptr<Observer> observer) = 0; // 添加观察者
virtual void detach(std::shared_ptr<Observer> observer) = 0; // 移除观察者
virtual void notify() = 0; // 通知所有观察者
virtual ~Subject() {
std::cout << "Subject destroyed" << std::endl;
}
};
// -------------------- 主题实现部分 --------------------
// 天气预报系统,持有观察者的 shared_ptr 列表
class WeatherStation : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers; // 所有订阅者
float temperature = 0.0f;
public:
// 注册观察者
void attach(std::shared_ptr<Observer> observer) override {
observers.push_back(observer);
}
// 注销观察者
void detach(std::shared_ptr<Observer> observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
// 通知所有观察者,调用它们的 update()
void notify() override {
for (auto& obs : observers) {
obs->update(temperature);
}
}
// 模拟温度变化,触发通知
void setTemperature(float t) {
temperature = t;
std::cout << "[WeatherStation] New temperature: " << t << "°C\n";
notify();
}
~WeatherStation() {
std::cout << "[WeatherStation] destroyed\n";
}
};
// -------------------- 观察者实现部分 --------------------
// App 实现观察者接口:当温度太高,会自动注销
class AppClient : public Observer, public std::enable_shared_from_this<AppClient> {
private:
std::shared_ptr<WeatherStation> station; // ❌ 持有 shared_ptr,形成循环引用
public:
// 构造函数,记录主题指针
AppClient(std::shared_ptr<WeatherStation> s) : station(s) {}
// 被通知时调用
void update(float temp) override {
std::cout << "[AppClient] Received temperature: " << temp << "°C\n";
// 如果温度过高,就自动注销
if (temp > 35.0f) {
std::cout << "[AppClient] Too hot! Unsubscribing...\n";
// ❌ 使用 shared_from_this() 注销自己,导致循环引用无法释放
station->detach(shared_from_this());
}
}
// 析构函数
~AppClient() {
std::cout << "[AppClient] destroyed\n";
}
};
// -------------------- 主函数 --------------------
int main() {
// 创建主题对象(天气系统)
std::shared_ptr<WeatherStation> station = std::make_shared<WeatherStation>();
// 创建观察者对象(App),并订阅
std::shared_ptr<AppClient> app = std::make_shared<AppClient>(station);
station->attach(app);
// 第一次更新温度,通知观察者
station->setTemperature(32.0f);
// 第二次更新温度,观察者会自动注销
// station->setTemperature(36.5f); //如果解除这一步,那么不会产生引用循环
// 程序即将结束
std::cout << "[main] Exiting...\n";
return 0;
}
输出:
结果:产生引用循环Observer 和 Subject 没有销毁 导致内存泄漏
[WeatherStation] New temperature: 32°C
[AppClient] Received temperature: 32°C
[main] Exiting...
----------------------------------------------------------------------------
//解除station->setTemperature(36.5f);
结果: 不会产生引用循环
[WeatherStation] New temperature: 32°C
[AppClient] Received temperature: 32°C
[WeatherStation] New temperature: 36.5°C
[AppClient] Received temperature: 36.5°C
[AppClient] Too hot! Unsubscribing...
[main] Exiting...
[AppClient] destroyed
Observer destroyed
[WeatherStation] destroyed
Subject destroyed
weak_ptr
改用 weak_ptr
其中一方,打破循环引用。
方式一 把AppClient里面的station改为weak_ptr类型,其他操作也做出相应改变
//方式一 把AppClient里面的station改为weak_ptr类型,其他操作也做出相应改变
#include
#include
#include
#include
// -------------------- 抽象接口部分 --------------------
// 观察者接口:所有 App 都要实现这个接口
class Observer {
public:
virtual void update(float temp) = 0; // 被通知时调用
virtual ~Observer() {
std::cout << "Observer destroyed" << std::endl;
}
};
// 主题(被观察者)接口:天气预报系统实现它
class Subject {
public:
virtual void attach(std::shared_ptr<Observer> observer) = 0; // 添加观察者
virtual void detach(std::shared_ptr<Observer> observer) = 0; // 移除观察者
virtual void notify() = 0; // 通知所有观察者
virtual ~Subject() {
std::cout << "Subject destroyed" << std::endl;
}
};
// -------------------- 主题实现部分 --------------------
// 天气预报系统,持有观察者的 shared_ptr 列表
class WeatherStation : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers; // 所有订阅者
float temperature = 0.0f;
public:
// 注册观察者
void attach(std::shared_ptr<Observer> observer) override {
observers.push_back(observer);
}
// 注销观察者
void detach(std::shared_ptr<Observer> observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
// 通知所有观察者,调用它们的 update()
void notify() override {
for (auto& obs : observers) {
obs->update(temperature);
}
}
// 模拟温度变化,触发通知
void setTemperature(float t) {
temperature = t;
std::cout << "[WeatherStation] New temperature: " << t << "°C\n";
notify();
}
~WeatherStation() {
std::cout << "[WeatherStation] destroyed\n";
}
};
// -------------------- 观察者实现部分 --------------------
// App 实现观察者接口:当温度太高,会自动注销
class AppClient : public Observer, public std::enable_shared_from_this<AppClient> {
private:
std::weak_ptr<WeatherStation> station; //修改1
public:
// 构造函数,记录主题指针
AppClient(std::shared_ptr<WeatherStation> s) : station(s) {}
// 被通知时调用
void update(float temp) override {
std::cout << "[AppClient] Received temperature: " << temp << "°C\n";
// 如果温度过高,就自动注销
if (temp > 35.0f) {
std::cout << "[AppClient] Too hot! Unsubscribing...\n";
if(!station.expired()) //修改2
{
station.lock()->detach(shared_from_this());
}
}
}
// 析构函数
~AppClient() {
std::cout << "[AppClient] destroyed\n";
}
};
// -------------------- 主函数 --------------------
int main() {
// 创建主题对象(天气系统)
std::shared_ptr<WeatherStation> station = std::make_shared<WeatherStation>();
// 创建观察者对象(App),并订阅
std::shared_ptr<AppClient> app = std::make_shared<AppClient>(station);
station->attach(app);
// 第一次更新温度,通知观察者
station->setTemperature(32.0f);
// 第二次更新温度,观察者会自动注销
// station->setTemperature(36.5f); //如果解除这一步,那么不会产生引用循环
// 程序即将结束
std::cout << "[main] Exiting...\n";
return 0;
}
输出:
[WeatherStation] New temperature: 32°C
[AppClient] Received temperature: 32°C
[main] Exiting...
[WeatherStation] destroyed
[AppClient] destroyed
Observer destroyed
Subject destroyed
方式二 把WeatherStation里面的observers改为weak_ptr类型,其他操作也做出相应改变
#include
#include
#include
#include
// -------------------- 抽象接口部分 --------------------
// 观察者接口:所有 App 都要实现这个接口
class Observer
{
public:
virtual void update(float temp) = 0; // 被通知时调用
virtual ~Observer()
{
std::cout << "Observer destroyed" << std::endl;
}
};
// 主题(被观察者)接口:天气预报系统实现它
class Subject
{
public:
virtual void attach(std::shared_ptr<Observer> observer) = 0; // 添加观察者
virtual void detach(std::shared_ptr<Observer> observer) = 0; // 移除观察者
virtual void notify() = 0; // 通知所有观察者
virtual ~Subject()
{
std::cout << "Subject destroyed" << std::endl;
}
};
// -------------------- 主题实现部分 --------------------
// 天气预报系统,持有观察者的 shared_ptr 列表
class WeatherStation : public Subject
{
private:
std::vector<std::weak_ptr<Observer>> observers; // 修改1
float temperature = 0.0f;
public:
// 注册观察者
void attach(std::shared_ptr<Observer> observer) override
{
observers.push_back(observer);
}
// 注销观察者
void detach(std::shared_ptr<Observer> observer) override
{
observers.erase( // 修改2
std::remove_if(observers.begin(), observers.end(),
[&observer](const std::weak_ptr<Observer> &wptr)
{
return !wptr.expired() && wptr.lock() == observer;
}),
observers.end());
}
// void detach(std::shared_ptr observer) override {
// std::cout << "[WeatherStation] detach\n";
// std::weak_ptr temp = observer;
// observers.erase(std::remove(observers.begin(), observers.end(), temp), observers.end());
// }
//如果改成这样的话,思路很接近核心,看起来像是合理的,但它其实 仍然不行
//原因就在于:std::weak_ptr 不支持 == 比较(哪怕是彼此之间),除非你先把它们 lock 成 shared_ptr
//两个 weak_ptr 即使指向相同的对象,它们也可能过期(expired)
//无法确定“空的”和“已销毁”的哪个该算“相等”
// 通知所有观察者,调用它们的 update()
void notify() override
{
for (auto &obs : observers)
{
if (!obs.expired()) // 修改3
obs.lock()->update(temperature);
}
}
// 模拟温度变化,触发通知
void setTemperature(float t)
{
temperature = t;
std::cout << "[WeatherStation] New temperature: " << t << "°C\n";
notify();
}
~WeatherStation()
{
std::cout << "[WeatherStation] destroyed\n";
}
};
// -------------------- 观察者实现部分 --------------------
// App 实现观察者接口:当温度太高,会自动注销
class AppClient : public Observer, public std::enable_shared_from_this<AppClient>
{
private:
std::shared_ptr<WeatherStation> station; // ❌ 持有 shared_ptr,形成循环引用
public:
// 构造函数,记录主题指针
AppClient(std::shared_ptr<WeatherStation> s) : station(s) {}
// 被通知时调用
void update(float temp) override
{
std::cout << "[AppClient] Received temperature: " << temp << "°C\n";
// 如果温度过高,就自动注销
if (temp > 35.0f)
{
std::cout << "[AppClient] Too hot! Unsubscribing...\n";
// ❌ 使用 shared_from_this() 注销自己,导致循环引用无法释放
station->detach(shared_from_this());
}
}
// 析构函数
~AppClient()
{
std::cout << "[AppClient] destroyed\n";
}
};
// -------------------- 主函数 --------------------
int main()
{
// 创建主题对象(天气系统)
std::shared_ptr<WeatherStation> station = std::make_shared<WeatherStation>();
// 创建观察者对象(App),并订阅
std::shared_ptr<AppClient> app = std::make_shared<AppClient>(station);
station->attach(app);
// 第一次更新温度,通知观察者
station->setTemperature(32.0f);
// 第二次更新温度,观察者会自动注销
// station->setTemperature(36.5f); //如果解除这一步,那么不会产生引用循环
// 程序即将结束
std::cout << "[main] Exiting...\n";
return 0;
}
输出:
[WeatherStation] New temperature: 32°C
[AppClient] Received temperature: 32°C
[main] Exiting...
[AppClient] destroyed
Observer destroyed
[WeatherStation] destroyed
Subject destroyed
weak_ptr
指向的对象weak_ptr
不能直接访问对象,需要通过 lock()
方法转换为 shared_ptr
,并检查对象是否仍然存在。
#include
#include
int main() {
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp = sp;
if (auto locked = wp.lock()) { //wp.lock() 会尝试安全地获取一个新的 shared_ptr,如果资源还存在,它就返回一个有效的 //shared_ptr,否则返回一个空的 shared_ptr 到时if(shared_ptr)会触发operator bool()函 // 数,也就是if(shared_ptr.bool()) == if(return shared_ptr.get() != nullptr;)
std::cout << "Value: " << *locked << std::endl;
} else {
std::cout << "Object no longer exists." << std::endl;
}
sp.reset(); // 释放资源
if (auto locked = wp.lock()) { // 再次尝试获取 shared_ptr
std::cout << "Value: " << *locked << std::endl;
} else {
std::cout << "Object no longer exists." << std::endl;
}
/* 效果一样!expired()
if(!wp.expired()) { // 再次尝试获取 shared_ptr
std::cout << "Value: " << *wp.lock() << std::endl;
} else {
std::cout << "Object no longer exists." << std::endl;
}
*/
return 0;
}
输出:
Value: 42
Object no longer exists.
解析:
wp.lock()
返回一个 shared_ptr
,如果对象依然存在,则有效。sp.reset()
释放资源后,wp.lock()
无法获取有效的 shared_ptr
。