首页 智能指针总结
文章
取消

智能指针总结

智能指针

C++11中智能指针的分类。是线程安全的嘛,如果不是使用时怎么处理。

在C++11中,提供了四种类型的智能指针:

  1. std::unique_ptr: 独特所有权,不能被复制,但可以通过std::move进行所有权转移。
  2. std::shared_ptr: 共享所有权,可以有多个shared_ptr指向同一个对象,shared_ptr使用引用计数来跟踪有多少个智能指针指向同一个资源,当最后一个shared_ptr被销毁时,资源也将被释放。
  3. std::weak_ptr: 弱引用,它可以指向一个由shared_ptr管理的对象,但它不参与引用计数,主要用来解决shared_ptr可能会引发的循环引用问题。
  4. std::auto_ptr: 这是一个被废弃的智能指针,它试图实现独占所有权,但其语义在C++标准中并不怎么清晰,因此在C++11中被std::unique_ptr所取代。

线程安全

关于线程安全性,除std::shared_ptr外,其他智能指针都不是线程安全的。注意,std::shared_ptr的线程安全性仅限于你可以在不同的线程中安全地使用单个shared_ptr的副本。然而,让多个线程同时访问同一shared_ptr实例(例如,一个线程读取,另一个线程写入)可能会导致数据竞争和未定义的行为。因此,如果在多线程环境中使用智能指针,你需要自己进行适当的同步。

以下是一个示例,展示如何在多线程环境中使用std::mutex来同步对std::shared_ptr的访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <memory>
#include <mutex>
#include <thread>

std::shared_ptr<int> p;
std::mutex mtx;

void thread_func() {
    std::lock_guard<std::mutex> lock(mtx);
    // 在这里安全地使用p
}

int main() {
    p = std::make_shared<int>(42);

    std::thread t1(thread_func);
    std::thread t2(thread_func);

    t1.join();
    t2.join();

    return 0;
}

在这个示例中,我们使用了std::mutexstd::lock_guard来保证在多个线程中对p的访问是互斥的,这样就可以避免数据竞争和未定义的行为。

std::shared_ptr的线程安全性仅限于可以在不同的线程中安全地使用单个shared_ptr的副本,正确使用的示例

以下是一个示例,展示std::shared_ptr线程安全性的正确使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <memory>
#include <thread>

void thread_func(std::shared_ptr<int> p) {
    // 在这里安全地使用p
}

int main() {
    std::shared_ptr<int> p = std::make_shared<int>(42);

    // 创建新的shared_ptr副本,并在新线程中使用它们
    std::thread t1(thread_func, p);
    std::thread t2(thread_func, p);

    t1.join();
    t2.join();

    return 0;
}

在这个示例中,我们在主线程中创建了一个shared_ptrp),然后在创建新线程时,我们将p传递给thread_func。每次thread_func被调用时,都会创建一个新的shared_ptr副本(即函数参数p),这个副本与原始的shared_ptr(主线程中的p)指向同一个对象。由于std::shared_ptr的线程安全性,这种用法是安全的,每个线程都有自己的shared_ptr副本,且不会发生数据竞争。

share ptr自己实现

  • 实现示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    
      #include <mutex>
        
      template <typename T>
      class shared_ptr
      {
      public:
          // 构造函数
          shared_ptr() : ptr_(nullptr), ref_count_(nullptr) {}
          shared_ptr(T *ptr) : ptr_(ptr), ref_count_(new int(1)) {}
          shared_ptr(const shared_ptr<T> &other) : ptr_(other.ptr_), ref_count_(other.ref_count_)
          {
              if (ref_count_)
                  increment_ref_count();
          }
        
          // 析构函数
          ~shared_ptr()
          {
              release();
          }
        
          // 赋值操作符
          shared_ptr<T> &operator=(const shared_ptr<T> &other)
          {
              if (this != &other)
              {
                  release();
                  ptr_ = other.ptr_;
                  ref_count_ = other.ref_count_;
                  if (ref_count_)
                      increment_ref_count();
              }
              return *this;
          }
        
          // 获取指针
          T *get() const
          {
              return ptr_;
          }
        
          // 获取引用计数
          int use_count() const
          {
              std::lock_guard<std::mutex> lock(mutex_);
              if (ref_count_)
                  return *ref_count_;
              else
                  return 0;
          }
        
          // 重载解引用操作符
          T &operator*() const
          {
              return *ptr_;
          }
        
          // 重载箭头操作符
          T *operator->() const
          {
              return ptr_;
          }
        
          // 重载布尔操作符
          operator bool() const
          {
              return ptr_ != nullptr;
          }
        
      private:
          T *ptr_;           // 指向被管理的对象
          int *ref_count_;   // 引用计数
          std::mutex mutex_; // 互斥量
        
          // 增加引用计数
          void increment_ref_count()
          {
              std::lock_guard<std::mutex> lock(mutex_);
              ++(*ref_count_);
          }
        
          // 释放资源
          void release()
          {
              std::lock_guard<std::mutex> lock(mutex_);
              if (ref_count_)
              {
                  --(*ref_count_);
                  if (*ref_count_ == 0)
                  {
                      delete ptr_;
                      delete ref_count_;
                  }
                  ptr_ = nullptr;
                  ref_count_ = nullptr;
              }
          }
      };
    

unique ptr简单实现

以下是 std::unique_ptr 的简化实现,以便你可以理解其工作原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template <typename T>
class unique_ptr {
public:
    explicit unique_ptr(T* ptr = nullptr) : ptr_(ptr) {}

    ~unique_ptr() { delete ptr_; }

    // 删除复制构造函数和复制赋值运算符
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

    // 提供移动构造函数和移动赋值运算符
    unique_ptr(unique_ptr&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
    unique_ptr& operator=(unique_ptr&& other) noexcept {
        if (this != &other) {
            delete ptr_;
            ptr_ = other.ptr_;
            other.ptr_ = nullptr;
        }
        return *this;
    }

    T* operator->() { return ptr_; }
    T& operator*() { return *ptr_; }

private:
    T* ptr_;
};

在上述代码中,当我们尝试复制一个 unique_ptr 时,编译器会报错,因为复制构造函数和复制赋值运算符已被删除。当我们尝试移动一个 unique_ptr 时(例如,将其传递给另一个函数,或从另一个函数返回它),移动构造函数或移动赋值运算符会被调用,它们会从源 unique_ptr 中删除指针,并在目标 unique_ptr 中设置该指针。这就确保了每个 unique_ptr 都有其独占的对象,从而实现了独占所有权。

本文由作者按照 CC BY 4.0 进行授权

C++上层应用(常见)

网络