MST
星途 面试题库

面试题:C++ 智能指针在对象间数据共享场景下的应用与内存管理

在C++对象间数据共享场景中,智能指针常被用于更好地管理内存。请详细分析不同类型智能指针(std::unique_ptr、std::shared_ptr、std::weak_ptr)在数据共享时的特性、适用场景以及如何避免内存泄漏和循环引用问题。请举例说明在复杂的数据共享结构中,如何合理使用智能指针来保证程序的正确性和高效性。
50.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

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函数结束时,ptrAptrB的引用计数降为0,AB对象被正确销毁,不会发生内存泄漏。