智能指针
- 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);