MST

星途 面试题库

面试题:C++智能指针在规避悬空指针问题中的应用细节

在C++中,智能指针(如std::unique_ptr、std::shared_ptr等)被广泛用于管理动态内存以避免悬空指针。请详细阐述std::unique_ptr和std::shared_ptr在解决悬空指针问题上的原理、实现机制以及它们在使用场景上的差异。
39.4万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

std::unique_ptr原理及实现机制

  1. 原理
    • std::unique_ptr采用独占式所有权模型。一个std::unique_ptr对象拥有对其所指向资源的唯一所有权。当std::unique_ptr对象被销毁时(例如离开其作用域),它所指向的资源也会被自动释放。这就确保了在任何时刻,只有一个指针可以访问该资源,从而避免了悬空指针的产生。因为当指向资源的std::unique_ptr被销毁,资源也被释放,不存在其他指针还能指向已释放资源的情况。
  2. 实现机制
    • std::unique_ptr通常通过一个裸指针来存储所指向的对象,并在其析构函数中释放该对象。例如,对于std::unique_ptr<int>,内部可能有一个int*类型的成员变量来存储指向int对象的地址。当std::unique_ptr对象生命周期结束,其析构函数会调用delete操作符来释放该int对象。它不支持拷贝构造和赋值操作符(普通的拷贝和赋值),以保证所有权的唯一性,但支持移动语义。移动语义允许将std::unique_ptr的所有权从一个对象转移到另一个对象,例如std::unique_ptr<int> p1 = std::make_unique<int>(5); std::unique_ptr<int> p2 = std::move(p1);,此时p1不再拥有对象,p2拥有原来p1所指向的对象。

std::shared_ptr原理及实现机制

  1. 原理
    • std::shared_ptr采用引用计数的方式来管理资源。多个std::shared_ptr对象可以指向同一个资源,每个std::shared_ptr对象内部都维护一个引用计数,记录当前有多少个std::shared_ptr指向该资源。当一个std::shared_ptr对象被创建并指向某个资源时,引用计数加1;当一个std::shared_ptr对象被销毁(例如离开作用域),引用计数减1。当引用计数变为0时,意味着没有任何std::shared_ptr再指向该资源,此时资源会被自动释放。这样就避免了悬空指针,因为只要有std::shared_ptr指向资源,资源就不会被释放,也就不会出现悬空指针。
  2. 实现机制
    • std::shared_ptr内部除了包含一个指向资源的指针外,还包含一个指向控制块的指针。控制块中存储着引用计数以及可能的弱引用计数(用于std::weak_ptr)等信息。每次创建、拷贝或销毁std::shared_ptr对象时,相应地调整引用计数。例如std::shared_ptr<int> p1 = std::make_shared<int>(5); std::shared_ptr<int> p2 = p1;,此时p1p2都指向同一个int对象,控制块中的引用计数变为2。当p1p2其中一个离开作用域时,引用计数减1,只有当引用计数变为0时,才会释放int对象所占用的内存。

使用场景差异

  1. std::unique_ptr
    • 适合独占资源场景:当一个资源只应由一个对象管理其生命周期时,std::unique_ptr是很好的选择。例如,一个函数内部创建了一个动态分配的对象,并且不希望将该对象的所有权传递出去,就可以使用std::unique_ptr。另外,对于一些性能敏感且资源只在局部使用的场景,std::unique_ptr由于没有引用计数的开销,性能更优。比如一个只在函数内部短暂使用的大型动态数组。
    • 用于移动语义场景:在需要将资源的所有权从一个对象转移到另一个对象时,std::unique_ptr的移动语义能高效地完成此操作,避免不必要的拷贝。例如,在函数返回一个动态分配的对象时,使用std::unique_ptr可以通过移动语义高效返回对象,而不进行拷贝。
  2. std::shared_ptr
    • 适合共享资源场景:当多个对象需要共享同一个资源的所有权时,std::shared_ptr是首选。比如在实现一个多线程环境下的共享数据结构,多个线程可能都需要访问和管理这个数据结构,std::shared_ptr可以确保只要还有线程在使用该数据结构,它就不会被释放。
    • 需要复杂对象生命周期管理场景:如果对象的生命周期依赖于多个不同的对象,std::shared_ptr通过引用计数可以更好地管理这种复杂的依赖关系。例如,一个对象A在不同的模块中被多个对象引用,使用std::shared_ptr可以自动处理对象A的释放,而不需要手动跟踪所有引用对象的生命周期。