MST

星途 面试题库

面试题:C++ 中的智能指针

请详细阐述 C++ 中智能指针(如 unique_ptr、shared_ptr、weak_ptr)的作用、原理及使用场景,同时说明 shared_ptr 是如何解决内存泄漏问题的,并举例说明在实际代码中三者的使用方式。
23.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

智能指针的作用

  1. 管理动态分配的内存:在 C++ 中,手动管理动态分配的内存容易出现内存泄漏、悬空指针等问题。智能指针提供了一种自动管理内存的机制,在对象不再被使用时自动释放其所占用的内存。
  2. 简化资源管理:不仅可以管理内存资源,还可以管理其他需要在使用后释放的资源,如文件句柄、网络连接等。

智能指针的原理

  1. unique_ptr
    • 原理:采用独占式拥有的策略,一个 unique_ptr 指针指向一块内存,并且只有它能释放这块内存。当 unique_ptr 对象被销毁时(例如离开其作用域),它所指向的内存会自动被释放。unique_ptr 不允许拷贝构造和赋值操作,但支持移动语义。
    • 实现方式:通常通过一个指针成员变量来指向动态分配的对象,并在析构函数中释放该对象。
  2. shared_ptr
    • 原理:采用引用计数的策略。多个 shared_ptr 可以指向同一个对象,对象的引用计数记录了当前指向该对象的 shared_ptr 的数量。当一个 shared_ptr 被创建或赋值时,引用计数增加;当一个 shared_ptr 被销毁(例如离开其作用域)或赋值为另一个对象时,引用计数减少。当引用计数降为 0 时,对象的内存被自动释放。
    • 实现方式:除了包含一个指向对象的指针外,还包含一个指向引用计数的指针。引用计数通常是一个堆上分配的计数器对象,通过智能指针间共享这个计数器对象来实现引用计数的管理。
  3. weak_ptr
    • 原理weak_ptr 是一种弱引用,它指向由 shared_ptr 管理的对象,但不会增加对象的引用计数。它主要用于解决 shared_ptr 之间的循环引用问题。weak_ptr 可以从一个 shared_ptr 或另一个 weak_ptr 创建。通过 weak_ptr 可以获取一个 shared_ptr,但在获取之前需要检查对象是否已经被释放(通过 expired() 方法)。
    • 实现方式weak_ptr 内部通常也包含一个指向对象的指针和一个指向引用计数的指针(与 shared_ptr 共享的引用计数),但它不会影响引用计数的值。

智能指针的使用场景

  1. unique_ptr
    • 场景:当你希望某个对象在代码中有唯一的所有者时,使用 unique_ptr。例如,一个函数返回一个动态分配的对象,并且调用者应该成为该对象的唯一所有者。
  2. shared_ptr
    • 场景:当需要多个对象共享对某块资源的所有权时,使用 shared_ptr。比如在实现缓存系统时,多个组件可能需要访问相同的缓存数据,这时可以用 shared_ptr 来管理缓存对象。
  3. weak_ptr
    • 场景:主要用于解决 shared_ptr 之间的循环引用问题。例如在双向链表中,节点之间的互相引用就可能导致循环引用,使用 weak_ptr 可以打破这种循环。

shared_ptr 解决内存泄漏问题的方式

shared_ptr 通过引用计数机制解决内存泄漏问题。只要有 shared_ptr 指向对象,对象的引用计数就大于 0,对象不会被释放。当所有指向对象的 shared_ptr 都被销毁(引用计数降为 0)时,对象的内存会自动被释放。这避免了手动管理内存时可能出现的忘记释放内存的情况,从而有效地防止了内存泄漏。

实际代码中三者的使用方式

  1. unique_ptr
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructed" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};

int main() {
    // 创建 unique_ptr
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();

    // 移动 unique_ptr
    std::unique_ptr<MyClass> ptr2 = std::move(ptr1);

    // 此时 ptr1 不再指向任何对象,ptr2 拥有 MyClass 对象的所有权
    return 0;
}
  1. shared_ptr
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructed" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};

int main() {
    // 创建 shared_ptr
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::shared_ptr<MyClass> ptr2 = ptr1;

    // 此时 ptr1 和 ptr2 都指向同一个 MyClass 对象,引用计数为 2
    std::cout << "Reference count: " << ptr1.use_count() << std::endl;

    // ptr2 离开作用域,引用计数减为 1
    {
        std::shared_ptr<MyClass> ptr3 = ptr1;
        std::cout << "Reference count inside block: " << ptr3.use_count() << std::endl;
    }
    std::cout << "Reference count outside block: " << ptr1.use_count() << std::endl;

    return 0;
}
  1. weak_ptr
#include <iostream>
#include <memory>

class MyClass;

class AnotherClass {
public:
    std::weak_ptr<MyClass> weakRef;
    ~AnotherClass() { std::cout << "AnotherClass destructed" << std::endl; }
};

class MyClass {
public:
    std::shared_ptr<AnotherClass> otherPtr;
    ~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};

int main() {
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::shared_ptr<AnotherClass> ptr2 = std::make_shared<AnotherClass>();

    ptr1->otherPtr = ptr2;
    ptr2->weakRef = ptr1;

    // 检查 weakRef 是否有效
    if (!ptr2->weakRef.expired()) {
        std::shared_ptr<MyClass> temp = ptr2->weakRef.lock();
        std::cout << "Weak pointer lock successful" << std::endl;
    } else {
        std::cout << "Weak pointer expired" << std::endl;
    }

    return 0;
}