智能指针
- nuique_ptr
- 特点
- 不允许拷贝构造和赋值
- 运算符重载-> () *
- unique_ptr 删除器仿写
- 删除文件
- 删除普通对象
- shared_ptr
- 特点
- 示意图
- 仿写shared_ptr
- 删除器部分特化
- 拷贝构造 移动构造 && 左值赋值 和移动赋值
- 完整实现
- weak_ptr
- 特点
- weak_ptr 实现
- 解决循环引用
- 弱指针一个应用
nuique_ptr
unique_ptr在要表达"专属所有权"的语义时使用,即unique_ptr指针永远′拥有"其指向的对象,所以unique_ptr是一个move-only类型,一个unique_ptr指针是无法被复制的,只能将"所有权"在两个unique_ptr指针之间转移,转移完成后原来的unique_ptr将被设为null;
智能指针是比原始指针更智能的类,解决悬空(dangling)指针或多次删除被指向对象,以及资源泄露问题,通常用来确保指针的寿命和其指向对象的寿命一致。智能指针虽然很智能,但容易被误用,智能也是有代价的。
特点
1 .基于排他所有权模式:两个指针不能指向同一个资源。这一块资源只能被一个指针指向
2.由于独占对象的拥有权,所以不提供拷贝构造函数和左值赋值函数重载。浅拷贝构造和浅赋值都是两个对象同时指向一个资源,
深拷贝,深赋值,同样的资源复制了一份,被两个unique对象指向,不行。
3.提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存指针是安全。
6.unique_ptr具有->和*运算符重载符,因此它可以像普通指针一样使用。
class PtrInt {
private:int* pval;
public:PtrInt(int x=0):pval(new int(x)){cout << "Create PtrInt" << this << endl;}~PtrInt() {delete pval;pval = nullptr;cout << "Destroy PtrInt" << this << endl;}PtrInt(const PtrInt& it) :pval(new int(10)) {if (it.pval != nullptr) {*pval = *it.pval;}cout << this<< "Copy Create PtrInt" <<&it<< endl;}PtrInt& operator=(const PtrInt& it) {if (this != &it) {delete pval;if (it.pval != nullptr) {pval = new int(*it.pval);}}cout << this << "operator=()" << &it << endl;return *this;}PtrInt(PtrInt&& it) :pval(it.pval) {it.pval = nullptr;cout<<this << "Move Create PtrInt" << &it << endl;}PtrInt& operator=(PtrInt&& it) {if (this != &it) {delete[]pval;pval = it.pval;it.pval = nullptr;}cout << this << "Move operatro=()" << &it << endl;return *this;}void SetValue(int x) { *pval = x; }int GetValue()const { return *pval; }void Print()const {if (pval != nullptr) {cout << *pval << endl;}}};template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}//唯一性指针没有拷贝构造和左值赋值my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}
};int main() {unique_ptr<PtrInt>pa(new PtrInt(10));pa->Print();return 0;
}
my_unique_ptrpa(new PtrInt(100));
不允许拷贝构造和赋值
唯一性智能指针不允许拷贝构造:浅拷贝pa pb的mPtr会指向同一个空间,析构时会把同一个地址释放两次,深拷贝就有了两份空间,应该只有一份
my_unique_ptr(const my_unique_ptr&) = delete;
也不允许赋值
my_unique_ptr& operator=(const my_unique_ptr&) = delete;
可以实现移动拷贝构造和移动赋值
移动之后,资源就不是以前的了,强行访问会崩溃
my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;
}//pa=move(pb); //unique_
my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;
}
运算符重载-> () *
pa是一个对象,访问对象的成员函数用.
为啥可以pa->
-> 都是用在结构体指针,->就是 先解引用,再 . 访问对象成员函数
这里重载了->
pa->Print();
pa.operator->()->Print();
等价
pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }
int main() {unique_ptr<Int>pa(new Int(100));pa->Print();pa.operator->()->Print();return 0;
}
class Int {int value;
public:Int(int x = 0) :value(x) {cout << "Create Int " << endl;}~Int() { cout << "Destroy Int " << endl; }void SetValue(int x) { value = x; }int GetValue()const {return value;}void Print()const { cout << value << endl; }
};template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }};int main() {unique_ptr<Int>pa(new Int(100));pa->Print();pa.operator->()->Print();return 0;
}
重载返回的mPtr ,*pa直接就是int对象,所以可以直接修改
int main() {unique_ptr<int>pa(new int(10));*pa = 100;pa.operator*() = 100;my_unique_ptr<Int>pb(new Int(10));//*pb返回的是一个Int对象 对象用. 访问成员函数Print()(*pb).Print();//上面已经解释过了,重载->pb->Print();return 0;
}
template<class _Ty>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;
private:pointer mPtr;
public:my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb); //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {delete mPtr;}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {delete mPtr;}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr, it.mPtr);}
};
unique_ptr 删除器仿写
对申请的系统资源,重写一个删除器类型,专门针对系统资源释放,如果是new
则需要delete 如果是文件fopen,则需要用fclose
删除文件
template<class _Ty,class _Dx=my_default_deleter<_Ty>>
class my_unique_ptr {
public:using pointer = _Ty*;using element_type = _Ty;using deleter_type = _Dx;
private:pointer mPtr;deleter_type deleter;
public:deleter_type get_deleter()const { return deleter; }my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb); //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {get_deleter() (mPtr);}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {get_deleter()(mPtr);}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr,ptr.mPtr);}
};
struct FileDeleter {void operator()(FILE*ptr)const {fclose(ptr);}
};
int main() {
my_unique_ptr<FILE>pa(fopen("xst.txt","w"));fprintf(pa.get(), "yhping\n");return 0;
}
删除普通对象
一组对象和单个对象的delete
template<class _Ty>
struct my_default_deleter {void operator()(_Ty* ptr)const {delete ptr;}
};
template<class _Ty>
struct my_default_deleter<_Ty[]> {void operator()(_Ty* ptr)const {delete[] ptr;}
};template<class _Ty, class _Dx = my_default_deleter<_Ty>>
class my_unique_ptr{
public:using pointer = _Ty*;using element_type = _Ty;using deleter_type = _Dx;
private:pointer mPtr;deleter_type deleter;
public:deleter_type get_deleter()const { return deleter; }my_unique_ptr(_Ty* p = nullptr) :mPtr(p){}my_unique_ptr(const my_unique_ptr&) = delete;my_unique_ptr& operator=(const my_unique_ptr&) = delete;my_unique_ptr(my_unique_ptr&& x) :mPtr(x.mPtr) {x.mPtr = nullptr;}//pa=move(pb); //unique_my_unique_ptr operator=(my_unique_ptr&& other) {if (this == &other)return *this;//my_unique_ptr(move(other)).swap(*this); 与下面三行等价delete mPtr;mPtr = other.mPtr;other.mPtr = nullptr;return *this;}~my_unique_ptr() {if (mPtr != nullptr) {get_deleter() (mPtr);}mPtr = nullptr;}pointer get()const { return mPtr; }_Ty& operator*()const { return *get(); }_Ty* operator->()const { return get(); }pointer release() {pointer old = mPtr;mPtr = nullptr;return old;}void reset(pointer ptr = nullptr) {if (mPtr != nullptr) {get_deleter()(mPtr);}mPtr = ptr; }void swap(my_unique_ptr& ptr) {swap(this->mPtr,ptr.mPtr);}
};class Int {int value;
public:Int(int x = 0) :value(x) {cout << "Create Int " << endl;}~Int() { cout << "Destroy Int " << endl; }void SetValue(int x) { value = x; }int GetValue()const {return value;}void Print()const { cout << value << endl; }
};int main() {my_unique_ptr<Int[]>pa(new Int[10]{1,2,3,4,5,6,7,8,9,10});for (int i = 0; i < 10; i++) {pa[i].Print();}return 0;
}
int main() {
std::unique_ptr<Int> pa(new Int(10));
std::unique_ptr<Int> pb = std::make_unique<Int>(20);
std::unique_ptr<Int[]> pca(new Int[10]{ 1,2,3,4,5,6,7,8,9,10 });
std::unique_ptr<Int[]> pcb = std::make_unique<Int[]>(10);//不能初始化
return 0;
}
shared_ptr
shared_ptr实现了共享所有权(shared ownership)方式来管理资源对象,这意味没有一个特定的std::shared_ptr拥有资源对象。相反,这些指向同一个资源对象的stdushared_ptr相互协作来确保该资源对象在不需要的时候被析构。
特点
1 .基于共享所有权模式:多个指针能够同时指向同一个资源。
2.基于共享所有权,使用引用计数控制块管理资源对象的生命期。
3.提供拷贝构造函数和赋值重载函数;提供移动构造和移动赋值函数。
4 .为了实现单个对象和一组对象的管理,添加了删除器类型。
5.在容器保存shared_ptr对象是安全。
6 .shared_ptr重载了operator->和operator*运算符,因此它可以像普通指针一样使用。
两个共享指针指向同一个资源,该资源的引用计数加一
示意图
和唯一性指针的区别是加了一个引用计数块,一个资源一个引用计数块
int main() {shared_ptr<int>pa(new int(10));cout << pa.use_count() << endl;shared_ptr<int>pb(pa);cout << pb.use_count();return 0;
}
atomic<int>_Uses;
原子操作,使其过程不能被打断。
~my_shared_ptr() {if (mRep != nullptr && mRep->Decref() == 0) {mDeleter(mPtr);delete mRep;}
资源需要用删除器删除,因为资源可能是API构建,释放也要API释放,而引用计数是new 所以释放 就是 delete
仿写shared_ptr
删除器部分特化
template<class _Ty>
struct My_Deleter
{
public:void operator()(_Ty* p) const{delete p;}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:void operator()(_Ty* p) const{delete []p;}
};
拷贝构造 移动构造 && 左值赋值 和移动赋值
拷贝构造实现的时候,两个shared_ptr指向的是同一个资源,同一个引用技术块,引用的uses+1
My_shared_ptr(const My_shared_ptr& x):mPtr(x.mPtr), mRep(x.mRep){if (mRep != nullptr){mRep->Incref();}}My_shared_ptr(My_shared_ptr&& other):mPtr(other.mPtr),mRep(other.mRep){mPtr = nullptr;mRep = nullptr;}
分析一下这个程序:左值赋值
My_shared_ptr(x) 类型名加括号,构建不具名对象,调用拷贝构造,引用计数加1,是个将亡值,然后析构将亡值,引用计数减1,发现uses为0 ,就删除了 pa指向的资源和引用计数块,
如果pa指向的资源uses不是1,那么就不会删除该资源和引用计数块
移动赋值
如果原来的资源的引用计数为1,移动后需要释放,不为1 就减1
移动赋值后,指针pb就跟原来的资源么有多了关系,pb->printf 会崩,而左值赋值,有关系
std::move(other) 转成右值,
My_shared_ptr& operator=(const My_shared_ptr& x){if (this == &x) return *this;My_shared_ptr(x).swap(*this);return *this;}My_shared_ptr& operator=(My_shared_ptr&& other){if (this == &other) return *this;My_shared_ptr(std::move(other)).swap(*this);return *this;}
完整实现
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include<atomic>
using namespace std;
class Int
{
private:int value;
public:Int(int x = 0) :value(x) { cout << "Create Int: " << endl; }Int(int x, int y) :value(x + y) { cout << "Create Int(int,int)" << endl; }~Int() { cout << "Destroy Int " <<value<<endl; }Int(const Int& it) :value(it.value){cout << "copy Create Int: " << this << endl;}Int& operator=(const Int& it){if (this != &it){value = it.value;}cout << "operator=()" << endl;return *this;}void PrintInt() const{cout << value << endl;}int& Value() { return value; }const int& Value() const { return value; }operator int() const { return value; }
};template<class _Ty>
struct My_Deleter
{
public:void operator()(_Ty* p) const{delete p;}
};
template<class _Ty>
struct My_Deleter<_Ty[]>
{
public:void operator()(_Ty* p) const{delete []p;}
};template<class _Ty>
class My_RefCount
{
public:using element_type = _Ty;using pointer = _Ty*;
private:_Ty* _Ptr;std::atomic<int> _Uses; // shared;std::atomic<int> _Weaks; // weak_ptr;
public:My_RefCount(_Ty* ptr = nullptr) :_Ptr(ptr), _Uses(0), _Weaks(0){if (_Ptr != nullptr){_Uses = 1;_Weaks = 1;}}~My_RefCount() = default;void Incref() { ++_Uses; }void Incwref() { ++_Weaks; }int Decref(){if (--_Uses == 0){Decwref();}return _Uses;}int Decwref(){return --_Weaks;}int _use_count() const{return _Uses.load();}
};template<class _Ty,class _Dx = My_Deleter<_Ty>>
class My_shared_ptr
{
public:using MyDeleter = _Dx;
private:_Ty* mPtr;My_RefCount<_Ty> * mRep;MyDeleter mDeleter;
public:My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr){if (mPtr != nullptr){mRep = new My_RefCount<_Ty>(mPtr);}}My_shared_ptr(const My_shared_ptr& x):mPtr(x.mPtr), mRep(x.mRep){if (mRep != nullptr){mRep->Incref();}}My_shared_ptr(My_shared_ptr&& other):mPtr(other.mPtr),mRep(other.mRep){mPtr = nullptr;mRep = nullptr;}My_shared_ptr& operator=(const My_shared_ptr& x){if (this == &x) return *this;My_shared_ptr(x).swap(*this);return *this;}My_shared_ptr& operator=(My_shared_ptr&& other){if (this == &other) return *this;My_shared_ptr(std::move(other)).swap(*this);return *this;}~My_shared_ptr(){if (nullptr != mRep && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;}mPtr = nullptr;mRep = nullptr;}int use_count() const{return mRep != nullptr ? mRep->_use_count() : 0;}_Ty * get() const { return mPtr; }_Ty& operator*() const { return *get(); }_Ty* operator->() const {return get();}operator bool() const { return mPtr != nullptr; }void swap(My_shared_ptr& other){std::swap(this->mPtr, other.mPtr);std::swap(this->mRep, other.mRep);}void reset(){if (mRep != nullptr && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;}mRep = nullptr;mPtr = nullptr;}void reset(_Ty* p){if (nullptr == p){reset();return;}if (mRep != nullptr && 0 == mRep->Decref()){mDeleter(mPtr);delete mRep;mRep = nullptr;}mPtr = p;if (mPtr != nullptr){mRep = new My_RefCount(mPtr);}}
};
weak_ptr
特点
弱引用指针weak_ptr是用来监视shared_ptr的生命周期,是shared_ptr的一个助手。weak_ptr没有重载操作符*和->,因为它不与shared_ptr共享指针,不能操作资源,主要是通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中管理的资源是否存在。weak_ptr还可以用来返回this 指针和解决循环引用的问题。
当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。可以通过use_count()方法来获得当前观测资源的引用计数。
weak_ptr 实现
template<class _Ty>
class My_weak_ptr
{
private:My_RefCount<_Ty>* mRep;
public:My_weak_ptr() :mRep(nullptr) {}My_weak_ptr(const My_shared_ptr<_Ty>& other){mRep = other.mRep;if (mRep != nullptr){mRep->Incwref();}}~My_weak_ptr(){if (mRep != nullptr){mRep->Decwref();}}
};
解决循环引用
class Parent;
class Child
{
public://std::shared_ptr<Parent> parent;std::weak_ptr<Parent> parent;Child() { cout << "Create Child" << endl; }~Child() { cout << "~Child" << endl; }
};
class Parent
{
public://std::shared_ptr<Child> child;std::weak_ptr<Child> child;Parent() { cout << "Create Parent" << endl; }~Parent() { cout << "~Parent" << endl; }void Hi() { cout << "hello yhping" << endl; }
};
int main()
{std::shared_ptr<Parent> pa(new Parent());std::shared_ptr<Child> cp(new Child());pa->child = cp;cp->parent = pa;return 0;
}
无法释放对象,因为use减为了1,调用析构函数时,无法删除 pa指向的资源和计数块
用弱指针就能解决该问题
弱引用指针只能对weaks加1,共享性指针只能对uses加一
只有当uses和weaks同时为0,才能删除该资源
uses为0可以析构资源,但是不能析构计数块,weaks为0 uses为0 才能析构计数块
过程就是:uses减1 weaks减1 释放child对象 ,该对象有mRep 所以 parent里面的weaks减1,此时析构child对象,此时析构pa,uses减1,为0,此时weaks为0,可以将该计数块析构,所以将parent对象释放,有弱引用指针,此时将最右边的计算计数块,weaks为0,
析构计数块,析构parent对象
弱指针一个应用
应用就是
删除父节点,子节点也会被自动释放删除,析构子节点,父节点不会被析构
template<class KeyType>
class BSTree
{
public:struct BstNode{std::shared_ptr<BstNode> leftchild;std::weak_ptr<BstNode> parent;std::shared_ptr<BstNode> rightchild;KeyType key;public:BstNode(const KeyType& kx):key(kx){}~BstNode(){}};
private:std::shared_ptr<BstNode> root;static void InOrder(std::shared_ptr<BstNode> ptr){if (ptr){InOrder(ptr->leftchild);cout << ptr->key << " ";InOrder(ptr->rightchild);}}
public:BSTree() :root(nullptr) {}~BSTree(){}bool Insert(const KeyType& kx){if (!root) // root.operator bool(){root = std::make_shared<BstNode>(kx); // Root;return true;}std::shared_ptr<BstNode> pa(nullptr);std::shared_ptr<BstNode> p = root;while (p && p->key != kx){pa = p;p = kx > p->key ? p->rightchild : p->leftchild;}if (p && p->key == kx){cout << "key 重复 " << endl;return false;}p = std::make_shared<BstNode>(kx);p->parent = pa;if (p->key > pa->key){pa->rightchild = p;}else{pa->leftchild = p;}return true;}void InOrder() const{InOrder(root);cout << endl;}
};
int main()
{int ar[] = { 53,17,78,9,45,65,87,23,81,94,88 };BSTree<Int> mytree;Int tmp;for (auto& x : ar){tmp.Value() = x;mytree.Insert(tmp);}mytree.InOrder();return 0;
}
shared_ptr<Int>pa(new Int(10));
shared_ptr<Int>pb=make_shared<Int>(20);
区别是pa 用了两次new 第二次开辟计数块的空间,而pb开辟一次,
weaks就是只要是指针指向,就算一个,弱指针和共享指针都可以,而uses必须是shared指针,才算数
pb空间只有等weaks为0才能释放,mPtr指向的和mPep指向的绑在一起了
My_shared_ptr(_Ty* ptr = nullptr) :mPtr(ptr), mRep(nullptr)
{if (mPtr != nullptr){mRep = new My_RefCount<_Ty>(mPtr);}
}
创建共享性指针的时候,有两个参数 ,指定所需的删除器,唯一性指针不具有,弱指针不具备删除对象
shared_ptr<Int>pa(new Int(10),DeleteInt);