面试题答案
一键面试std::unique_ptr
- 原理:独占所指向的对象,通过移动语义来转移所有权,不能进行拷贝构造和拷贝赋值,只能移动构造和移动赋值。当
std::unique_ptr
对象被销毁时,它所指向的对象也会被自动销毁。 - 适用场景:适用于对象只需要被一个所有者管理的场景,比如函数内部创建的局部对象,该对象不需要在函数外部共享。
- 实现机制:通常实现为一个封装了原始指针的类,内部重载了
delete
操作符来释放所指向的对象。当std::unique_ptr
对象离开作用域时,析构函数会被调用从而释放内存。
std::shared_ptr
- 原理:通过引用计数的方式来管理对象的生命周期。多个
std::shared_ptr
可以指向同一个对象,每当有新的std::shared_ptr
指向该对象时,引用计数加1;当std::shared_ptr
被销毁时,引用计数减1。当引用计数为0时,对象被自动销毁。 - 适用场景:适用于对象需要被多个地方共享的场景,比如在多个模块间传递同一个对象的指针,并且这些模块都需要对该对象进行操作。
- 实现机制:内部维护一个指向对象的指针以及一个指向引用计数的指针。构造、拷贝构造、赋值等操作会修改引用计数,析构函数会在引用计数为0时释放对象内存。
std::weak_ptr
- 原理:它是一种弱引用,指向由
std::shared_ptr
管理的对象,但不增加引用计数。主要用于解决std::shared_ptr
之间的循环引用问题。当std::shared_ptr
管理的对象被销毁后,std::weak_ptr
会自动变为空指针。 - 适用场景:当存在可能导致循环引用的场景时,比如双向链表节点间的互相引用。也适用于需要检测对象是否存在但不影响其生命周期的场景。
- 实现机制:内部也维护一个指向对象的指针,但没有直接增加引用计数的操作。通过
lock()
成员函数可以获取一个std::shared_ptr
,如果对象已被销毁,lock()
会返回一个空的std::shared_ptr
。
在复杂对象层次结构中的使用策略
- 独占对象:对于希望独占的对象,使用
std::unique_ptr
。例如,在对象层次结构的叶子节点,如果这些叶子节点不需要被其他对象共享,std::unique_ptr
可以有效地管理它们的生命周期,确保当包含它们的父对象销毁或离开作用域时,叶子节点对象也被正确销毁。 - 共享对象:对于需要被多个地方共享的对象,使用
std::shared_ptr
。例如,在对象层次结构中有一些中间节点对象,多个分支都需要访问这些对象时,std::shared_ptr
可以保证只要有任何一个地方还在使用该对象,它就不会被销毁。 - 处理循环引用:如果对象层次结构中存在可能导致循环引用的情况,比如两个对象互相持有对方的引用,这时可以在其中一个方向上使用
std::weak_ptr
。例如,A对象持有B对象的std::shared_ptr
,而B对象持有A对象的std::weak_ptr
,这样就可以避免循环引用导致的内存泄漏问题。同时,B对象可以通过std::weak_ptr
的lock()
函数来判断A对象是否还存在并获取其std::shared_ptr
以进行操作。