每种智能指针都是以类模版的方式实现的,shared_ptr<T>(其中T表示指针指向的具体数据类型)的位于<memory>头文件。
shared_ptr是C++11提供的一种智能指针类,它可以在任何地方不使用时自动删除相关指针,从而帮助彻底解决内存泄露和悬空指针的问题。不同的shared_pre对象可以与相同的指针相关联,且在内部使用引用计数机制。
每个shared_ptr对象的内部指向两个内存位置:指向对象的指针;用于控制引用计数数据的指针。
1.创建智能指针
shared_ptr<T>类模板中,提供了多种实用的构造函数,有以下几种方式创建智能指针
1)不传实参,创建空智能指针
shared_ptr<int> p1;
2)传入空指针nullptr,创建智能指针
shared_ptr<int> p2(nullptr);
空的shared_ptr指针,其初始引用计数是0,不是1
3)创建智能指针时,明确其指向
shared_ptr<int> p3(new int(10));
创建了一个shared_ptr智能指针,其指向一块存有10这个int类型数据的堆内存空间
C++11标准中还提供了make_shared<T>模版函数,可以用于初始化shared_ptr智能指针
shared_ptr<int> p3=make_shadre<int>(10);
这2种方式创建的P3是相同的
—-
shared_ptr<T>模板提供拷贝构造函数、移动构造函数
4)使用拷贝构造函数
shared_ptr<int> p4(p3);
shared_ptr<int> p4=p3;
p3和p4都是shared_ptr类型的智能指针,因此可以用p3来初始化P4,由于p3是左值,因此会调用拷贝构造函数。如果p3为空智能指针,则p4也为空智能指针,其引用计数初始值为0;反之,则表明p4和p3指向同一块堆内存,同时该堆空间的引用计数会加1
注意:同一个普通指针不能同时为多个shadre_ptr对象赋值,否则会导致程序发生崩溃
int *ptr=new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr);/错误
5)使用移动构造函数
shared_ptr<int> p5(move(p4));
shared_ptr<int> p5=move(p4);
对于move(p4)而言,该函数会强制将p4转换成对应的右值,因此初始化p5调用的是移动构造函数。和拷贝构造函数不同,用move(p4)初始化p5,会使得p5拥有p4的堆内存,而p4则变成了空智能指针
6)使用reset()
reset有两个操作,reset()会使引用计数减1;调用reset(new xxx())重新赋值时,智能指针首先生成新对象,然后将旧对象的引用计数减1(如果发现引用计数为0,则析构旧对象),然后重新将对象的指针交给智能指针
shared_ptr<int> p5(new int(9));
cout<<*p5<<endl;
p5.reset(new int(11));
cout<<*p5<<endl;
7)在初始化shared_ptr智能指针时,可以自定义所指向的堆内存的释放规则,这样堆内存的引用计数为0时,会优先调用我们自定义的释放规则。
在某些场景下,自定义释放规则很有必要,比如,对于申请的动态数组来说,shared_ptr指针默认的释放规则是不支持释放数组的,只能自定义对应的释放规则,才能正确地释放所申请的堆内存
对于申请的动态数组,释放规则可以使用C++11标准中提供的default_delete<T>模板类,也可以自定义释放规则
//方式1:
shared_ptr<int> p6(new int[10],default_delete<int[]>());
方式2:
void deleteInt(int *p)
{
delete[] p;
}
shared_ptr<int> p7(new int[10],deleteInt);
同时,借助Lambda表达式,还可以像如下进行初始化,效果是一样的
shared_ptr<int> p7(new int[10],[](int *p){delete[] p;});
2.shared_ptr<T>模板类提供的成员方法
1)operator=()
重载赋值号,使得同一类型的shared_ptr智能指针可以相互赋值
2)operator*()
重载*号,获取当前shared_ptr智能指针对象指向的数据
3)operator->()
重载->号,当智能指针指向的数据类型为自定义的结构体时,通过->运算符可以获取其内部的指定成员
4)swap()
交换两个相同类型的shared_ptr智能指针的内容
5)reset()
当函数没有实参时,该函数会使当前shared_ptr所指堆内容的引用计数减1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的shared_ptr对象会获得该存储空间的所有权,并且引用计数的初始值为1。
6)get()
获得shared_ptr对象内部包含的普通指针
7)use_count()
返回当前shared_ptr对象(包括它)指向相同的所有shared_ptr对象的数量
8)unique()
判断当前shared_ptr对象指向的堆内存,是否不再有其他shared_ptr对象指向它
9)operator bool()
判断当前shared_ptr对象是否为空智能指针,如果是空指令,返回false,否则返回true
如下面是一个shared_ptr的例子
#include <iostream>
#include<memory.h>
using namespace std;
class A
{
private:
int a;
int b;
public:
A(int x,int y)
{
a=x;
b=y;
cout<<"执行了构造函数"<<endl;
}
~A()
{
cout<<"执行了析构函数"<<endl;
}
void func()
{
cout<<"a="<<a<<" b="<<b<<endl;
}
};
int main() {
std::cout << "Hello, World!" << std::endl;
shared_ptr<int> p1(new int(10));
shared_ptr<int> p2(p1);
cout<<*p2<<endl;
p1.reset();
if(p1)
cout<<"p1不为空"<<endl;
else
cout<<"p1为空"<<endl;
cout<<*p2<<endl;
cout<<p2.use_count()<<endl;
cout<<"另一个test"<<endl;
shared_ptr<A> p3(new A(3,5));
p3->func();
shared_ptr<A> p4(p3);
cout<<"p3的use_count() "<<p3.use_count()<<endl;
cout<<"p4的use_count() "<<p4.use_count()<<endl;
cout<<p3.unique()<<endl;
cout<<p3.operator bool()<<endl;
cout<<"p3执行reset之后:"<<endl;
p3.reset();
cout<<"p3的use_count() "<<p3.use_count()<<endl;
cout<<"p4的use_count() "<<p4.use_count()<<endl;
cout<<p3.unique()<<endl;
cout<<p3.operator bool()<<endl;
p4.reset();
cout<<"p4执行reset之后:"<<endl;
cout<<p3.unique()<<endl;
cout<<p4.unique()<<endl;
return 0;
}