手写智能指针shared_ptr
手写智能指针shared_ptr
本文尝试手写shared_ptr,此外如果使用memory库中的make_shared相对于shared_ptr有以下好处:
- make_shared会在一次内存分配中同时分配对象本体和引用计数
- 减少安全异常,两次操作变一个
- 高效的引用计数管理,一个内存块中存储对象和引用计数,指针访问时减少额外的缓存访问
- 代码简洁不用new
#include<iostream>
#include<memory>
template<typename T>
class ShareCount{
private:
T* ptr;//指针管理的对象
int count;//引用计数
//禁止拷贝构造函数和赋值
ShareCount(const ShareCount&)=delete;
ShareCount& operator=(const ShareCount&)=delete;
public:
//构造函数
ShareCount(T *p):ptr(p),count(1){};
//析构
~ShareCount(){delete ptr;};
//增加引用计数
void increment(){
count++;
};
//减少引用计数
void decrement(){
//如果计数为0则删除对象
if(--count==0){
delete this;
}
};
//返回指针管理的对象
T* get() const{
return ptr;
}
};
template<typename T>
class shared_ptr{
private:
T* ptr;//指向指针管理的对象
ShareCount<T> *countPtr;//管理智能指针引用计数器类对象
public:
//构造函数
shared_ptr(T* p = nullptr):ptr(p){
//调用构造
if(p){
countPtr=new ShareCount<T>(p);
}else{
countPtr=nullptr;
}
}
//拷贝构造 不新开辟空间 只添加计数
shared_ptr(const shared_ptr& other):ptr(other.ptr),countPtr(other.countPtr){
//如果智能指针引用对象存在则计数器加1
if(countPtr){
countPtr->increment();
}
}
//移动构造 std::move等转为右值引用时调用,右值&&及临时存在的对象
//移动构造参数不加const,并删除原来的资源
shared_ptr(shared_ptr&& other):ptr(other.ptr),countPtr(other.countPtr){
other.ptr=nullptr;
other.countPtr=nullptr;
}
//析构函数
~shared_ptr(){
if(countPtr){
countPtr->decrement();
}
}
//重写shared_ptr的-> 返回的是T* 指向指针管理的对象
T* operator->() const {
return ptr;
}
//解引用返回的是T* 指针的解引用
T& operator*() const{
return *ptr;
}
//reset
void reset(T* p = nullptr){
//如果两个指针不相同,指针改变
if(p!=ptr){
//计数器不为空,且指针改变(减少一个),计数器应该减去1
if(countPtr){
countPtr->decrement();
}
}
//相同或者不同都指向新的空间,只是不同会导致计数器减一
//如果为空则为nullptr
ptr=p;
if(p){
//如果p不为空,创建新的计数器
countPtr=new ShareCount<T>(p);
}else{
countPtr=nullptr;
}
}
//公共方法get返回指针
T* get() const{
return ptr;
}
};
struct data{
int mydata;
data(int d){
mydata=d;
}
};
int main(){
shared_ptr<int> ptr1(new int(10));
//调用拷贝构造,ptr和countPtr都是同一个,只是countPtr increment调用了count+1
shared_ptr<int> ptr2=ptr1;
std::cout<<"ptr1:"<<(*ptr1)<<std::endl;
std::cout<<"ptr2:"<<(*ptr2)<<std::endl;
ptr1.reset();
std::cout<<"ptr2:"<<(*ptr2)<<std::endl;
shared_ptr<int> ptr3=std::move(ptr2);
// ptr2为空 以下代码Segmentation fault
// cout<<"ptr2:"<<(*ptr2)<<endl;
std::cout<<"ptr3:"<<(*(ptr3.get()))<<std::endl;
//memory中std::shared_ptr也有类似效果
std::shared_ptr<int> p1(new int(10));
std::cout<<"std::p1:"<<(*p1)<<std::endl;
//或者使用memory中make_shared就不用new了
// 1.make_shared会在一次内存分配中同时分配对象本体和引用计数
// 2.减少安全异常,两次操作变一个
// 3.高效的引用计数管理,一个内存块中存储对象和引用计数,减少额外指针的访问
// 4.代码简洁不用new
std::shared_ptr<int> p2=std::make_shared<int>(10);
std::cout<<"std::p2:"<<(*p2)<<std::endl;
return 0;
}