MST

星途 面试题库

面试题:C++中智能指针与析构函数调用场景的深度剖析

在使用不同类型智能指针(如std::unique_ptr、std::shared_ptr、std::weak_ptr)的情况下,析构函数的调用场景会有哪些变化?考虑循环引用等复杂场景,如何确保析构函数能按预期执行,避免内存泄漏?请通过详细代码和原理分析进行阐述。
50.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

std::unique_ptr

  1. 原理std::unique_ptr 是一种独占所有权的智能指针,同一时间只有一个 std::unique_ptr 可以指向一个对象。当 std::unique_ptr 离开其作用域或被显式释放时,它所指向的对象会被自动销毁,即调用对象的析构函数。
  2. 代码示例
#include <iostream>
#include <memory>

class MyClass {
public:
    ~MyClass() {
        std::cout << "MyClass destructor called" << std::endl;
    }
};

int main() {
    {
        std::unique_ptr<MyClass> ptr(new MyClass());
    } // 当ptr离开这个作用域,MyClass的析构函数被调用
    return 0;
}

std::shared_ptr

  1. 原理std::shared_ptr 允许多个智能指针共享同一个对象的所有权。它使用引用计数来管理对象的生命周期,当引用计数降为0时,所指向的对象会被自动销毁,调用其析构函数。
  2. 代码示例
#include <iostream>
#include <memory>

class MyClass {
public:
    ~MyClass() {
        std::cout << "MyClass destructor called" << std::endl;
    }
};

int main() {
    {
        std::shared_ptr<MyClass> ptr1(new MyClass());
        std::shared_ptr<MyClass> ptr2 = ptr1;
    } // 当ptr1和ptr2都离开作用域,引用计数降为0,MyClass的析构函数被调用
    return 0;
}
  1. 循环引用问题:当两个或多个 std::shared_ptr 之间形成循环引用时,引用计数永远不会降为0,导致内存泄漏。
#include <iostream>
#include <memory>

class B;

class A {
public:
    std::shared_ptr<B> b;
    ~A() {
        std::cout << "A destructor called" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a;
    ~B() {
        std::cout << "B destructor called" << std::endl;
    }
};

int main() {
    {
        std::shared_ptr<A> ptrA(new A());
        std::shared_ptr<B> ptrB(new B());
        ptrA->b = ptrB;
        ptrB->a = ptrA;
    } // 这里A和B的析构函数都不会被调用,造成内存泄漏
    return 0;
}

std::weak_ptr

  1. 原理std::weak_ptr 是一种弱引用,它指向由 std::shared_ptr 管理的对象,但不会增加对象的引用计数。std::weak_ptr 主要用于解决 std::shared_ptr 的循环引用问题。
  2. 解决循环引用代码示例
#include <iostream>
#include <memory>

class B;

class A {
public:
    std::weak_ptr<B> b;
    ~A() {
        std::cout << "A destructor called" << std::endl;
    }
};

class B {
public:
    std::weak_ptr<A> a;
    ~B() {
        std::cout << "B destructor called" << std::endl;
    }
};

int main() {
    {
        std::shared_ptr<A> ptrA(new A());
        std::shared_ptr<B> ptrB(new B());
        ptrA->b = ptrB;
        ptrB->a = ptrA;
    } // 此时A和B的析构函数会被调用,避免了内存泄漏
    return 0;
}

总结

  • std::unique_ptr:适合独占资源场景,析构函数在离开作用域或手动释放时调用。
  • std::shared_ptr:用于共享资源场景,但要注意循环引用问题。
  • std::weak_ptr:配合 std::shared_ptr 使用,解决循环引用问题,确保析构函数按预期执行,避免内存泄漏。