面试题答案
一键面试1. std::unique_ptr
- 特性:独占式拥有资源,同一时间只能有一个
std::unique_ptr
指向给定对象。当std::unique_ptr
被销毁时,它所指向的对象也会被自动销毁。不能进行拷贝构造和拷贝赋值,但可以进行移动构造和移动赋值。 - 适用场景:适用于对象所有权明确,不需要共享,一个对象在其生命周期内只有一个所有者的场景。例如,函数内部创建的临时对象,该对象只在函数内部使用,函数结束时对象应该被销毁。
- 避免内存泄漏:由于
std::unique_ptr
在析构时自动释放资源,只要std::unique_ptr
对象能正常析构(例如,不发生异常提前退出函数),就不会发生内存泄漏。
2. std::shared_ptr
- 特性:允许多个
std::shared_ptr
指向同一个对象,通过引用计数来管理对象的生命周期。当引用计数为0时,对象被自动销毁。支持拷贝构造和拷贝赋值,每次拷贝或赋值,引用计数会增加;当std::shared_ptr
被销毁时,引用计数会减少。 - 适用场景:适用于需要共享对象所有权的场景,比如多个对象需要访问同一个数据结构,这些对象都对该数据结构有“拥有权”,且希望该数据结构在最后一个使用者结束使用时才被销毁。
- 避免内存泄漏和循环引用:
- 避免内存泄漏:只要所有指向对象的
std::shared_ptr
都能正常析构,引用计数最终会变为0,对象会被正确释放。 - 避免循环引用:循环引用是指两个或多个
std::shared_ptr
相互指向,导致引用计数永远不会为0,从而造成内存泄漏。可以使用std::weak_ptr
来解决循环引用问题。
- 避免内存泄漏:只要所有指向对象的
3. std::weak_ptr
- 特性:是一种弱引用,指向由
std::shared_ptr
管理的对象,但不增加对象的引用计数。它可以用来观察对象是否存在,但不会阻止对象被销毁。通过lock()
成员函数可以尝试获取一个std::shared_ptr
,如果对象已被销毁,lock()
会返回一个空的std::shared_ptr
。 - 适用场景:用于解决
std::shared_ptr
的循环引用问题,以及在需要观察对象是否存在但又不想影响其生命周期的场景。例如,实现一个缓存系统,缓存中的对象由std::shared_ptr
管理,外部有一些弱引用指向这些对象,用于检查对象是否还在缓存中。 - 避免循环引用:在可能出现循环引用的场景中,将其中一个方向的引用改为
std::weak_ptr
。例如,A类和B类相互引用,将其中一个类(如B类)中指向另一个类(A类)的指针改为std::weak_ptr
,这样就打破了循环引用。
4. 复杂数据共享结构中智能指针的使用示例
#include <memory>
#include <iostream>
class B;
class A {
public:
std::shared_ptr<B> b;
~A() { std::cout << "A destroyed" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a;
~B() { std::cout << "B destroyed" << std::endl; }
};
void test() {
std::shared_ptr<A> ptrA = std::make_shared<A>();
std::shared_ptr<B> ptrB = std::make_shared<B>();
ptrA->b = ptrB;
ptrB->a = ptrA;
}
int main() {
test();
// 程序执行到这里,ptrA和ptrB离开作用域,引用计数变为0,A和B对象会被正确销毁
// 不会发生内存泄漏,因为使用std::weak_ptr打破了循环引用
return 0;
}
在上述代码中,A
类和B
类形成了数据共享结构。通过将B
类中对A
类的引用改为std::weak_ptr
,避免了循环引用,保证了程序的正确性和高效性。在test
函数结束时,ptrA
和ptrB
的引用计数降为0,A
和B
对象被正确销毁,不会发生内存泄漏。