uinque_ptr
分享指针
CG
- c++ 智能指针深度探索:彻底掌握智能指针
- https://github.com/RyanChen-cn/CppFunction/blob/main/miniSmartpointers/miniauto_ptr.h
template <typename T>
class miniunique_ptr
{
public:
// 构造函数
explicit miniunique_ptr(T *p = nullptr) : pointer(p) {}
// 禁用左值构造
template <typename U>
miniunique_ptr(miniunique_ptr<U> &) = delete;
// 允许右值构造
template <typename U>
miniunique_ptr(miniunique_ptr<U> &&p) : pointer(p.release()) {}
// 析构函数
~miniunique_ptr()
{
delete pointer;
}
// 模拟-> * 符号
T *operator->() const
{
return pointer;
}
T &operator*() const
{
return *pointer;
}
// 危险操作 返回原始指针
T *get() const
{
return pointer;
}
// 放弃智能指针管理,并没有销毁对象
T *release()
{
T *old_ptr = pointer;
pointer = nullptr;
return old_ptr;
}
// 控制另一个对象,销毁之前保存的对象 注意可能会出现自己重新控制自己的问题
void reset(T *p = nullptr)
{
if (p != pointer)
{
delete pointer;
pointer = p;
}
}
// 禁止左值赋值
template <typename U>
miniunique_ptr<T> &operator=(miniunique_ptr<U> &rp) = delete;
// 允许右值赋值
// 重写赋值操作符,这里是于unique_ptr不同的地方,额外再加入一个U是为了增加弹性
// 等于操作符会释放右操作数到左操作数
template <typename U>
miniunique_ptr<T> &operator=(miniunique_ptr<U> &&rp)
{
if (this->pointer != rp.pointer)
{
reset(rp.release());
}
return *this;
}
private:
T *pointer;
};
move
std::move
是 C++ 标准库中的一个函数模板,位于 <utility>
头文件中。它用于将对象的所有权从一个对象(即源对象)转移到另一个对象(即目标对象)。
在实际使用中,std::move
将对象的状态标记为“可移动(movable)”,从而允许编译器执行移动语义的优化。它并不实际迁移或复制对象的数据,而只是将对象标记为可用于移动操作的临时状态。
std::move
的作用是将对象的右值引用返回给调用者,使调用者可以使用该右值引用来执行移动或转移操作。通过将右值引用传递给相关的函数或构造函数,可以实现资源的有效转移和避免不必要的数据复制。
以下是 std::move
的示例用法:
#include <iostream>
#include <utility>
int main() {
int x = 10;
int y = std::move(x); // 将 x 的所有权转移到 y
std::cout << "x: " << x << std::endl; // 输出未定义的值,因为 x 的所有权已经转移
std::cout << "y: " << y << std::endl; // 输出 10,因为 y 获取了 x 的值
return 0;
}
在示例中,我们使用 std::move
将变量 x
的所有权转移到变量 y
,而不是执行数据的实际复制。通过这种方式,我们可以高效地将资源所有权转移到新的对象,避免不必要的开销。
请注意,使用 std::move
后,源对象应被视为处于有效但未定义的状态,不应再对其进行访问。移动后的目标对象将具有有效和定义明确的状态。
unique_ptr与move:(move(x),其目的是将一个左值引用 x 转换为右值引用)
#include <iostream>
#include <memory>
class Tmp {
public:
Tmp(){};
Tmp(int a): __a(a){};
~Tmp(){};
virtual void print__a() { std::cout << "value = " << __a << std::endl; }
private:
int __a;
};
int main() {
std::unique_ptr<Tmp> up1(new Tmp(5));
std::unique_ptr<Tmp> up2;
up2 = std::move(up1); // 移动 up1 到 up2
up2->print__a(); // 输出 5
return 0;
}
shared_ptr对象之间的转移和拷贝的区别
- 转移 shared_ptr 所有权的操作更高效,因为它只是修改指针的所有权,并不需要增减引用计数。
- 拷贝 shared_ptr 的操作需要增加引用计数,因此会对引用计数造成额外的开销。
- 在需要转移资源所有权、不再需要原始 shared_ptr 指向资源的情况下,使用转移更为适合。
- 在需要资源共享、保持原始 shared_ptr 的有效性的情况下,使用拷贝。
- 注意:C++11 中的 shared_ptr 默认支持 move 操作,即可以通过 std::move 来转移所有权。如果您使用的是早期版本的 C++(如 C++98/03),可能需要使用智能指针类库(如 Boost)来实现 shared_ptr 的转移操作。
#include <iostream>
#include <memory>
int main() {
// 创建一个 shared_ptr 对象
std::shared_ptr<int> sourcePtr = std::make_shared<int>(42);
// 转移 shared_ptr 的所有权
std::shared_ptr<int> destPtr = std::move(sourcePtr);
// sourcePtr 现在为空
std::cout << "sourcePtr: " << sourcePtr << std::endl;
// destPtr 持有指向相同内存的共享资源
std::cout << "destPtr: " << destPtr << std::endl;
return 0;
}
# sourcePtr: 0
# destPtr: 0x56430331dec0
智能指针在多线程情况下使用的注意事项
在多线程情况下使用智能指针时,有几个注意事项需要考虑,以确保线程安全性和避免悬垂指针的问题。
互斥访问:在多个线程中同时使用相同的智能指针时,需要确保对指针进行互斥访问,以保证线程安全。可以使用互斥锁(mutex)或其他同步机制,例如std::lock_guard或std::unique_lock。
#include <memory>
#include <iostream>
#include <mutex>
#include <thread>
std::shared_ptr<int> sharedPtr;
std::mutex mutex;
void ThreadFunction(int a)
{
std::lock_guard<std::mutex> lock(mutex);
std::this_thread::sleep_for(std::chrono::seconds(a));
// 使用 sharedPtr 进行读写操作
*sharedPtr = a;
std::cout<< *sharedPtr<<std::endl;
}
int main()
{
sharedPtr = std::make_shared<int>(42);
// 启动多个线程访问智能指针
std::thread t1(ThreadFunction,2);
std::thread t2(ThreadFunction,1);
std::thread t3(ThreadFunction,3);
t1.join();t3.join();t2.join();
}
std::atomic<std::shared_ptr> atomicCounter; : error: static assertion failed: std::atomic requires a trivially copy type
- atomic仅支持科平凡赋值对象,即不包含virtual函数
多线程中使用unique_ptr
是的,可以在多线程环境中使用std::unique_ptr。std::unique_ptr是一种独占所有权的智能指针,可以确保在多线程环境下正确管理内存资源。
以下是一个示例,展示了多线程环境下使用std::unique_ptr的情况:
#include <iostream>
#include <memory>
#include <thread>
void ThreadFunction(std::unique_ptr<int> uniquePtr)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread: " << *uniquePtr << std::endl;
}
int main()
{
std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);
std::thread t1(ThreadFunction, std::move(uniquePtr));
t1.join();
return 0;
}
在上述示例中,我们创建了一个std::unique_ptr对象,并将其作为参数传递给另一个线程的函数ThreadFunction。通过std::move函数进行所有权的转移,确保uniquePtr在主线程中不再持有资源。
在新线程中,我们打印出通过uniquePtr访问的整数值。在这个例子中,由于std::unique_ptr具有独占所有权,所以在传递所有权后,uniquePtr被置为null,并且在新线程中访问uniquePtr会导致异常。因此,在多线程环境中使用std::unique_ptr时,需要确保正确地进行所有权的转移,避免使用已转移所有权的uniquePtr。