智能指针
unique_ptr
作用域指针,不能复制
栈分配指针,当死亡时,自动释放所管理的内存,无需显示调用delete
1 2 3
| std::unique_ptr<int> p1 = std::make_unique<int>(10);
std::unique_ptr<int> p2 = std::move(p1);
|
只能显示调用构造函数,因为其构造函数有explicit关键字,没有了构造函数的隐式转换
最好的调用还是使用make_unique会捕获异常,不会产生悬空指针问题
shared_ptr
追踪引用计数,如果引用为0,则释放内存
需要分配内存用于计数
1
| shared_ptr<Entity> p = make_shared<Entity>();
|
作用主要包括:
- 自动管理动态分配的对象,避免手动调用
delete。
- 支持共享所有权,让多个
shared_ptr 可以安全地访问同一对象。
- 借助引用计数机制,实现对象的生命周期控制,当最后一个
shared_ptr 销毁后,自行释放资源。
unique_ptr:独占所有权,不支持多个指针管理同一个对象;更轻量且不存在循环引用问题。
weak_ptr:用于观察 shared_ptr 管理的对象,不增加引用计数;主要用于辅助 shared_ptr,避免循环引用的问题。
std::weak_ptr
std::weak_ptr 是 C++11 引入的一种智能指针,和 std::shared_ptr 一起使用,用于避免 循环引用 问题,同时提供了一种对 std::shared_ptr 所管理对象的弱引用(non-owning reference)。它不改变所管理对象的引用计数。
- 循环引用问题:
在使用 std::shared_ptr 时,如果两个对象互相以 shared_ptr 引用彼此,会导致内存泄漏,因为它们的引用计数无法递减到 0。
shared_ptr 通过引用计数管理对象的生命周期,当引用计数为 0 时,自动释放对象。
- 如果存在循环引用,两个对象会始终持有对方,这样它们的引用计数永远不会减为 0,因此无法释放内存。
- 非拥有性的弱引用:
有时候,一个对象只需要 “观察” 对另一个对象的引用,而无需控制它的生命周期。这时使用 std::weak_ptr 是更合理的选择。
std::weak_ptr 提供了一种临时、不影响生命周期的引用,从而解决了上述问题。
总结
std::weak_ptr 的主要使用场景包括:
- 解决
std::shared_ptr 的循环引用问题。
- 跨组件之间的非拥有性引用,例如缓存对象的管理。
- 事件监听器或回调函数,避免悬垂指针的产生。
- 在弱引用需求场景下提供更加灵活的资源管理,而不是一味增加强引用计数。
std::weak_ptr 的特点
- 不控制对象的生命周期:
- 检测对象是否已销毁:
- 可以通过调用
weak_ptr 的 expired() 方法来检查被引用的对象是否已经销毁。
- 使用
lock() 转换为 shared_ptr:
- 如果需要安全地访问被引用的对象,可以调用
weak_ptr 的 lock() 方法,返回一个临时的 shared_ptr。如果对象已销毁,lock() 会返回一个空指针。
假设我们有两个类 A 和 B,它们通过 std::shared_ptr 互相引用。如果没有使用 weak_ptr,将会发生循环引用,导致内存泄漏。
示例代码(循环引用问题):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <memory>
class B;
class A { public: std::shared_ptr<B> ptrB; ~A() { std::cout << "A destroyed" << std::endl; } };
class B { public: std::shared_ptr<A> ptrA; ~B() { std::cout << "B destroyed" << std::endl; } };
int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->ptrB = b; b->ptrA = a;
return 0; }
|
运行结果:
1
| # 没有输出,因为 `A` 和 `B` 无法正常析构,发生内存泄漏。
|
解决循环引用的正确做法:
将其中一个引用改为 std::weak_ptr,避免两个对象互相增加引用计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class B;
class A { public: std::weak_ptr<B> ptrB; ~A() { std::cout << "A destroyed" << std::endl; } };
class B { public: std::shared_ptr<A> ptrA; ~B() { std::cout << "B destroyed" << std::endl; } };
int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->ptrB = b; b->ptrA = a;
return 0; }
|
运行结果:
通过将某一侧的引用改为 std::weak_ptr,打破了循环引用。
跨组件间的弱引用
如果某些对象之间并无强依赖关系,但仍需临时引用,则可以使用 std::weak_ptr。
示例 1:缓存管理
在缓存系统中,如果一个对象的存在依赖于被缓存的内容,则可以使用 std::weak_ptr:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <iostream> #include <memory> #include <unordered_map> #include <string>
class CachedObject { public: CachedObject(std::string name) : name(name) { std::cout << name << " created" << std::endl; } ~CachedObject() { std::cout << name << " destroyed" << std::endl; } void printName() { std::cout << "Object name: " << name << std::endl; }
private: std::string name; };
int main() { std::unordered_map<std::string, std::weak_ptr<CachedObject>> cache;
{ auto obj1 = std::make_shared<CachedObject>("Object1"); cache["key1"] = obj1;
auto obj2 = std::make_shared<CachedObject>("Object2"); cache["key2"] = obj2;
if (auto obj = cache["key1"].lock()) { obj->printName(); } }
if (cache["key1"].expired()) { std::cout << "Object1 no longer exists" << std::endl; }
return 0; }
|
运行结果:
1 2 3 4 5 6
| Object1 created Object2 created Object name: Object1 Object2 destroyed Object1 destroyed Object1 no longer exists
|
事件回调(防止悬垂引用)
如果某类对象注册了一个事件监听器或回调函数,而监听器的生命周期可能比被观察的对象短,那么可以使用 std::weak_ptr 避免访问悬垂的指针。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> #include <memory> #include <functional>
class EventSource;
class Listener { public: Listener(std::shared_ptr<EventSource> source) : source(source) {} void onEvent() { if (auto src = source.lock()) { std::cout << "Event handled" << std::endl; } else { std::cout << "Source no longer exists" << std::endl; } }
private: std::weak_ptr<EventSource> source; };
class EventSource : public std::enable_shared_from_this<EventSource> { public: void fireEvent() { if (listener) listener(); }
void setListener(std::function<void()> callback) { listener = callback; }
private: std::function<void()> listener; };
int main() { std::shared_ptr<EventSource> source = std::make_shared<EventSource>(); { auto listener = std::make_shared<Listener>(source); source->setListener([listener]() { listener->onEvent(); });
source->fireEvent(); }
source->fireEvent();
return 0; }
|