MST
星途 面试题库

面试题:C++ std::shared_ptr的线程安全性及自定义删除器

在多线程环境下,std::shared_ptr的引用计数操作是否线程安全?如果不是,如何确保其在多线程中的正确使用?另外,请描述如何为std::shared_ptr定义并使用自定义删除器,这样做有什么实际意义?
11.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. std::shared_ptr引用计数操作的线程安全性

    • std::shared_ptr的引用计数操作是线程安全的。这意味着多个线程可以同时对std::shared_ptr进行构造、析构以及赋值操作,而不会导致数据竞争问题。当一个std::shared_ptr对象被创建、复制、移动或销毁时,其内部的引用计数会被原子地更新。例如:
    #include <iostream>
    #include <memory>
    #include <thread>
    
    std::shared_ptr<int> ptr;
    
    void threadFunction() {
        std::shared_ptr<int> localPtr = ptr;
        std::cout << "In thread, reference count: " << localPtr.use_count() << std::endl;
    }
    
    int main() {
        ptr = std::make_shared<int>(42);
        std::thread t1(threadFunction);
        std::thread t2(threadFunction);
    
        t1.join();
        t2.join();
    
        return 0;
    }
    
    • 在上述代码中,多个线程可以安全地操作ptr的引用计数。
  2. 确保std::shared_ptr在多线程中的正确使用

    • 虽然引用计数操作是线程安全的,但如果涉及到对std::shared_ptr所指向对象的读写操作,仍然需要额外的同步机制。例如,可以使用std::mutex来保护对指向对象的访问:
    #include <iostream>
    #include <memory>
    #include <thread>
    #include <mutex>
    
    std::shared_ptr<int> ptr;
    std::mutex mtx;
    
    void threadFunction() {
        std::unique_lock<std::mutex> lock(mtx);
        std::shared_ptr<int> localPtr = ptr;
        lock.unlock();
        if (localPtr) {
            std::cout << "In thread, value: " << *localPtr << std::endl;
        }
    }
    
    int main() {
        ptr = std::make_shared<int>(42);
        std::thread t1(threadFunction);
        std::thread t2(threadFunction);
    
        t1.join();
        t2.join();
    
        return 0;
    }
    
    • 在这个例子中,std::mutex用于保护对ptr的访问,避免在读取ptr所指向对象时发生数据竞争。
  3. std::shared_ptr定义并使用自定义删除器

    • 定义自定义删除器的语法:
      • 可以通过在构造std::shared_ptr时传入一个可调用对象(函数指针、lambda表达式或仿函数)来定义自定义删除器。例如,使用函数指针作为自定义删除器:
      #include <iostream>
      #include <memory>
      
      void customDeleter(int* ptr) {
          std::cout << "Custom deleter called" << std::endl;
          delete ptr;
      }
      
      int main() {
          std::shared_ptr<int> ptr(new int(42), customDeleter);
          return 0;
      }
      
      • 使用lambda表达式作为自定义删除器:
      #include <iostream>
      #include <memory>
      
      int main() {
          std::shared_ptr<int> ptr(new int(42), [](int* p) {
              std::cout << "Custom deleter called" << std::endl;
              delete p;
          });
          return 0;
      }
      
    • 使用自定义删除器的实际意义:
      • 资源管理灵活性:可以实现非标准的资源释放逻辑。例如,对于动态分配的数组,可以使用delete[]而不是delete来释放内存。对于一些需要特殊清理操作的资源,如文件句柄(除了关闭文件,可能还需要做一些日志记录等操作),可以在自定义删除器中实现这些额外的清理逻辑。
      • 适配不同的资源管理策略:在一些情况下,可能需要使用第三方库提供的资源释放函数,自定义删除器可以方便地适配这种情况,使std::shared_ptr能够正确管理这些资源。