智能指针
static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。 初始化的时候自动初始化为0
为什么使用智能指针:
因为使用new和detele存在内存所有权不清晰,容易产生不销毁和多销毁的情况,不同的智能指针提供的不同的析构函数,在调用函数结束以后会有不同的动作。
什么是内存所有权不清晰,有什么影响
- 例子1
int* fun()
{
int a = 3;
return &a;
}
int main()
{
int* P = fun();
}
例子1fun函数和main函数都有变量a存储区的所有权;
- 例子2
int* fun()
{
static int a = 3;
return &a;
}
int main()
{
int* P = fun();
}
这时候变量a存储与静态存储区,在程序终止的时候才释放;这时候fun函数和main函数都不具备变量a存储区的所有权;
share_ptr
每种智能指针都是以类模板的方式实现的,shared_ptr 也不例外。shared_ptr(其中 T 表示指针指向的具体数据类型)的定义位于头文件;引用计数机制是基于引用计数的共享内存解决方案
什么是引用计数
shared_ptr有默认的回收机制delete,但是也可以自定义回收机制。另外每当使用一次,引用计数就会加1,当本次引用循环结束,引用计数减一,当计数为0的时候就会触发回收机制。
默认的回收机制
template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );
shared_ptr管理的对象不需要额外使用delete进行内存释放,当shared_ptr引用计数为0
以后会调用默认的detele删除机制,
指定回收机制
#include<iostream>
#include<new>
#include<memory>
using namespace std;
void fun(int* x)
{
cout << "fun is called" << endl;
delete x;
}
int main()
{
shared_ptr<int> ptr(new int(5),fun);//计数为零以后调用fun 、
return 0;
}
自定义回收机制的应用
get
将shared_ptr< int>类型转换为int* 类型,相当于只拿出智能指针指向的储存区,并不拿出智能指针的计数值的储存;
- 例子
shared_ptr<int> fun()
{
shared_ptr<int> res(new int(100));
return res;
}
int main()
{
auto y = fun();
cout << *y << endl; //100
cout << typeid(y).name() << endl;//class std::shared_ptr<int>
cout << *(y.get()) << endl;//100
cout << typeid(y.get()).name() << endl;//int * __ptr64
return 0;
}
- 例子2
void fun(int* A)
{return;}
int main()
{
shared_ptr<int> N(new int(3));
fun(N);//不合法
fun(N.get());
}
reset
把原始的资源释放,将新的资源关联上;把指针指向的原始资源释放,把新的内容放入这块内存;当该资源的计数为0时,reset才生效;如果不为零,就无效
make_shared
std::shared_ptr在实现的时候使用的refcount技术,因此内部会有一个计数器(控制块,用来管理数据)和一个指针,指向数据。因此在执行 std::shared_ptr< A> p2(new A) 的时候,首先会申请数据的内存,然后申请内控制块,因此是两次内存申请,而 std::make_shared< A>() 则是只执行一次内存申请,将数据和控制块的申请放到一起;
指向数组的share_ptr的定义
shared_ptr<T[]> A(new int[5]); //c++17
auto A = make_shared<int[]>(5); //c++20
禁止使用detele销魂shared_ptr管理的对象
会发生重复释放的错误
shared_ptr<int> A(new int(3));
detele A.get();//经过这个语句以后,指针A指向的资源被释放,指针悬空指向一个没有被分配的对象;但是计数还在,计数减1,计数为零,又一次释放该资源
其他重复释放的错误
shared_ptr<int> A(new int(3));
shared_ptr<int> B(A.get());//把内存信息传给B,B拥有内存资源的所有权,且指针B计数为1;
程序结束的时候,B的计数减为0释放该内存;但是A也是指向这个内存空间,又一次释放;
unique_ptr
与shared_ptr的共享内存不同的是,unique_ptr是独占内存的解决方案,不支持复制但是支持移动,可以为unique_ptr指定回收逻辑
unique_ptr<int> y(new int(4));
unique_ptr<int> z = y;
这段代码报错,因为y独占内存,不能与指针z共享。与shared_ptr不同
资源所有权的移动,move方法
进行资源所有权转交
- 例子1
unique_ptr<int> y(new int(4));
unique_ptr<int> z = move(y);
move使得x放弃资源,将资源使用权移交与z
2. 例子2
#include<iostream>
#include<new>
#include<memory>
using namespace std;
unique_ptr<int> fun()
{
unique_ptr<int> res = new int(3);
return res;//隐形的转换
}
int main()
{
unique_ptr<int> y = fun();//将开辟的资源通过一个临时对象进行转交
return 0;
}
- 例子3
#include<iostream>
#include<new>
#include<vector>
#include<memory>
using namespace std;
int main()
{
unique_ptr<int> y(new int(4));
cout << y << endl;
unique_ptr<int> z = move(y);
cout << y << endl;
cout << z << endl;
return 0;
}
output:
0081E700
00000000
0081E700
make_unique
给unique_ptr指定回收逻辑,与shared-ptr不同。unique_ptr
模板中有两个参数,shared_ptr模板只有一个参数
#include<iostream>
#include<new>
#include<memory>
using namespace std;
void fun(int* ptr)
{
delete ptr;
}
int main()
{
shared_ptr<int> y(new int(3), fun));
unique_ptr<int,decltype(fun)> x(new int(3),fun);
return 0;
}
weak_ptr
防止循环引用而引入的指针,基于shared_ptr构造
- 例子1
#include<iostream>
#include<new>
#include<memory>
using namespace std;
struct Str
{
shared_ptr<Str> m_nei;
~Str()
{
cout << "Str is called" << endl;
}
};
int main()
{
shared_ptr<Str> x(new Str{});
cout << x.use_count() << endl;
shared_ptr<Str> y(new Str{});
cout << y.use_count() << endl;
x->m_nei = y;
cout << y.use_count() << endl;
//y->m_nei = x;
//cout << x.use_count() << endl;
return 0;
}
output:
1
1
2
Str is called
Str is called
- 例子2
#include<iostream>
#include<new>
#include<memory>
using namespace std;
struct Str
{
shared_ptr<Str> m_nei;
~Str()//析构函数,当函数销毁以后来调用
{
cout << "Str is called" << endl;
}
};
int main()
{
shared_ptr<Str> x(new Str{});
cout << x.use_count() << endl;
shared_ptr<Str> y(new Str{});
cout << y.use_count() << endl;
x->m_nei = y;
cout << y.use_count() << endl;
y->m_nei = x;
cout << x.use_count() << endl;
return 0;
}
output:
1
1
2
2
并没有出现计数为零的情况,那么shared_ptr对象也就不会销毁进而执行析构函数所以这里就出现了 循环引用;这样导致没办法销毁内存。所以引入了waker_ptr;
因为例子2形成了一个环,所以导致计数不能减到0;
为了防止循环引用,使用weak_ptr;在初始化的时候不会增加相应资源的引用计数
- 例子3
struct Str
{
weak_ptr<Str> m_nei;
~Str() { cout << "Str is called" << endl; }
};
int main()
{
shared_ptr<Str> x(new Str{});
shared_ptr<Str> y(new Str{});
cout << x.use_count() << endl;//1
cout << y.use_count() << endl;//1
x->m_nei = y;
y->m_nei = x;
cout << x.use_count() << endl;//1
cout << y.use_count() << endl;//1
return 0;
}
初始化weak_ptr的时候,资源y的引用计数不变化;但是这样的话智能指针的计数优势就没了,因此引入了lock方法
lock
std::shared_ptr lock() const noexcept;
lock方法返回一个shared_ptr,所以说weak_ptr是由shared_ptr构成的
struct Str
{
weak_ptr<Str> m_nei;
~Str(){cout << "Str is called" << endl;}
};
int main()
{
shared_ptr<Str> x(new Str{});
{
shared_ptr<Str> y(new Str{});
x->m_nei = y;
y->m_nei = x;
}
auto ptr = x->m_nei.lock();
if (ptr) {cout << "can access pointer" << endl;}
else {cout << "cannot access pointer" << endl;}
return 0;
}
output:
Str is called
cannot access pointer
Str is called
- 例子2
struct Str
{
weak_ptr<Str> m_nei;
~Str(){cout << "Str is called" << endl;}
};
int main()
{
shared_ptr<Str> x(new Str{});
shared_ptr<Str> y(new Str{});
x->m_nei = y;
y->m_nei = x;
auto ptr = x->m_nei.lock();
if (ptr) {cout << "can access pointer" << endl;}
else {cout << "cannot access pointer" << endl;}
return 0;
}
//output:
can access pointer
Str is called
Str is called