1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586#include<iostream>#include<mutex>#include<memory>#include<shared_mutex>using namespace std;//写时复制包装器模板类template<typename T>class CowPtr{private: mutable shared_ptr<T> ptr; //mutable 关键字允许在 const 方法中修改这个成员变量 mutable shared_mutex mutex;//mutable 关键字允许在 const 方法中使用锁,shared_mutex为可读锁public: CowPtr(T* p = nullptr):ptr(p) {}//构造函数 CowPtr(const shared_ptr<T>& p):ptr(p){}//通过shared_ptr构造 CowPtr(const CowPtr& other) { lock_guard<shared_mutex>lock(other.mutex); ptr = other.ptr; } CowPtr(CowPtr&& other)noexcept//noexcept用来声明一个函数不会抛出任何异常 { /* * 这里的 CowPtr(CowPtr&& other) 表示 接受一个右值引用 类型的 other 参数。它通常用于“移动”资源而不是“复制”资源。 std::move(other.ptr) 将 other.ptr 从一个左值变为右值,并允许移动其内部资源,而不是复制它们 */ lock_guard<shared_mutex>lock(other.mutex);//确保在移动资源时,不会发生数据竞争 ptr = move(other.ptr); } //赋值操作符 CowPtr& operator=(const CowPtr& other) { if (this != &other) { unique_lock<shared_mutex>lock_this(mutex, defer_lock); unique_lock<shared_mutex>lock_other(other.mutex, defer_lock); /* * std::unique_lock 比 lock_guard 更灵活,允许延迟锁定(std::defer_lock) std::lock 函数可以同时锁定多个互斥锁,且能避免死锁 */ lock(lock_this, lock_other); ptr = other.ptr; } return *this; } // 移动赋值操作符 CowPtr& operator=(CowPtr&& other) noexcept { if (this != &other) { std::unique_lock<shared_mutex> lock_this(mutex, std::defer_lock); std::unique_lock<shared_mutex> lock_other(other.mutex, std::defer_lock); std::lock(lock_this, lock_other); ptr = std::move(other.ptr); } return *this; } // 析构函数 ~CowPtr() = default; // 读取操作 - 返回常量引用 const T& read() const { std::lock_guard<std::shared_mutex> lock(mutex); return *ptr; } // 写入操作 - 返回可修改的引用,需要时执行写时复制 T& write() { std::lock_guard<shared_mutex> lock(mutex); // 检查引用计数,如果不是唯一拥有者,则复制一份 if (!ptr.unique()) { ptr = std::make_shared<T>(*ptr); } return *ptr; } /* * 写时复制策略,它的核心目的是 保证多个线程能够安全地共享资源,直到其中一个线程需要修改该资源时,才会复制该资源 * 实质上,当有需要写入的操作时,首先判断是否为唯一引用,如果是,那么直接修改不会影响 * 如果不是唯一引用,那么则复制一份数据的副本,原数据与副本各自独立,复制过程中用锁保证不会被其他线程修改 * 然后将副本修改,独自占用,其他读的操作,仍然读取原始数据 */};