实际开发中:
1)用户开辟了堆空间,没有及时释放堆空间资源,可能会造成内存泄露
2)用户开辟了堆空间,也及时释放了堆空间资源,但该空间其它对象还在使用,会出现程序崩溃的现象
作用:
用于帮助用户去管理堆空间
智能指针体现出智能化(可以帮助用户自动管理堆内存空间),当引用计数机制等于0的时候,智能指针会自动释放所管理的堆空间资源
在C++中智能指针它不是一个指针,他是一个类(模板类),该类重载了指针的运算,例如:->、*、++、--等等,形象的称呼“智能指针”
shared_ptr:共享指针
weak_ptr:弱指针
unique_ptr:唯一指针
C++11标准舍弃:auto_ptr:自动指针
该指针可以允许其它多个智能指针一起管理同一块内存空间
如何使用:
创建共享指针,共享指针会有两个空间,一个空间是管理的堆空间,另外一个空间是存放计数值
计数值:
共享指针引入的引用计数机制,计数值里存放的是有多少个智能指针管理同一块堆空间,即新增一个智能指针管理堆空间,那么引用计数+1,减少一个智能指针管理堆空间,那么引用计数-1
方法:
注:多个共享指针使用同一个引用计数空间
share1.use_count()//查看引用计数值
share1.swap(share4);//交换管理的空间
share1.reset();//释放管理权
get()//获取所管理的堆空间的地址
#include "iostream"
using namespace std;
//智能指针shared_ptr(共享智能指针)
int main()
{
int* p = new int(10);//使用裸指针开辟的堆空间
int* q = new int(50);
//将我们开辟的指针p指向的空间 交给共享智能指针share1来管理
shared_ptr share1(p);
shared_ptrshare2 = share1;//share2也来管理p指针指向的空间
shared_ptrshare3(share1);//拷贝构造函数
shared_ptr share4(q);//share4也来管q指针指向的空间
cout << "引用计数值:" << share1.use_count() << endl; //3
cout << endl;
cout << "空间中的值:" << *share1 << endl;
cout << endl;
//查看空间地址
cout << "share1管理空间的地址:" << share1 << endl;
cout << "share2管理空间的地址:" << share2 << endl;
cout << "share3管理空间的地址:" << share3 << endl;
cout << "p指向空间的地址:" << p << endl;
cout << endl;
//.get()也可以查看管理的地址
cout << "share1管理空间的地址:" << share1.get() << endl;
cout << "share2管理空间的地址:" << share2.get() << endl;
cout << "share3管理空间的地址:" << share3.get() << endl;
cout << "p指向空间的地址:" << p << endl;
cout << endl;
//交换两个共享指针所管理的堆空间权力
cout << "share1管理空间的地址:" << share1.get() << endl;
cout << "share4管理空间的地址:" << share4.get() << endl;
cout << endl;
share1.swap(share4);//交换管理的空间
cout << "share1管理空间的地址:" << share1.get() << endl;
cout << "share4管理空间的地址:" << share4.get() << endl;
cout << endl;
//释放堆空间的管理权
share1.reset();//释放管理权,这里释放的share1是q的空间权力,因为我们前面交换了share1和share4的管理的空间
cout << "share1管理空间的地址:" << share1.get() << endl;
cout << endl;
cout << "引用计数值:" << share2.use_count() << endl; //3
return 0;
}
//智能指针shared_ptr(共享智能指针),管理自定义类型
class People {
public:
int number = 5;
};
int main()
{
People* people = new People;//将类的实例开辟在堆上
shared_ptr share1(people);
cout << "number的值为:" << share1->number << endl;
return 0;
}
例如:
解引用
取成员操作
引用计数操作等
#include "iostream"
using namespace std;
// 手动实现shared_ ptr共享指针
//例如:
//解引用
//取成员操作
//引用计数操作等
class People {
public:
int num = 123;
};
template class share_ptr {
public:
share_ptr(T* data) {//构造函数
this->ptr = data;
refCount = new int(1);
}
//重载* 解引用操作
T operator * () {
return *ptr;
}
//重载取成员操作
T* operator ->() {
return ptr;
}
// 引用计数操作
int use_count() const {
return *refCount;//返回计数值
}
// 拷贝构造函数
share_ptr(const share_ptr& other)
{
ptr = other.ptr;
refCount = other.refCount;
(*other.refCount)++; // 引用计数加 1
}
// 重载赋值运算符 //传入要赋值的对象
void operator =(const share_ptr& other)
{
// 防止自赋值
if (this != &other)
{
// 释放资源
ptr = other.ptr;
refCount = other.refCount;
(*other.refCount)++; // 引用计数加 1
}
}
// 释放资源
void my_release()
{
(*refCount)--;
if (*refCount == 0)
{
delete ptr;
delete refCount;
}
}
//析构函数
~share_ptr() {
my_release();
}
T* ptr; //存储要管理的对象空间
int* refCount; //计数值
};
int main()
{
int* p = new int(22);
share_ptr share1(p);
cout << "值为:" << *share1 << endl;
cout << "引用计数值为:" << share1.use_count() << endl;
People* people = new People;
share_ptr share2(people);
cout << "people类的num值为:" << share2->num << endl;
cout << "引用计数值为:" << share2.use_count() << endl;
//调用拷贝构造函数
share_ptr share3(share1);
cout << "引用计数值为:" << share1.use_count() << endl;
//调用释放函数
share3.my_release();
cout << "引用计数值为:" << share1.use_count() << endl;
share_ptr share4 = share1;
share_ptr share5 = share1;
cout << "引用计数值为:" << share1.use_count() << endl; //3
//调用释放函数
share4.my_release();
cout << "引用计数值为:" << share1.use_count() << endl;//2
return 0;
}
weak_ptr提出是辅助shared_ptr使用
问题:
使用共享指针会造成资源被相互占用,而得不到释放的问题
提出:
弱指针来解决该问题
#include "iostream"
using namespace std;
//先演示shared_ptr指针的问题
class Person;//提前声明以下Person类防止,报未定义错误(以为我们在people中使用了Person类)
class People {
public:
shared_ptr share1;
~People() {
cout << "释放People的资源" << endl;
}
};
class Person {
public:
shared_ptr share2;
~Person() {
cout << "释放Person的资源" << endl;
}
};
int main()
{
//实例化两个对象
People* people = new People;
Person* person = new Person;
//时share11 share22分别管理对应的空间
shared_ptr share11(people);//管理people
shared_ptr share22(person);//管理person
//接着给对象中的属性赋值,使其属性也管理相应的空间
people->share1 = share22;//使people中的share1属性管理person空间
person->share2 = share11;//使person中的share2属性管理people空间
//2 ,并且两个类的析构函数都没调用,空间没有得到释放
//这是因为在类中share1管理了person空间, 而share2有管理了people空间,双方都在等待对方使用完成后释放空间
//两个对象相互持有彼此的 shared_ptr,导致了循环引用,从而阻止了析构函数的调用。
cout << "引用计数值:" << share11.use_count() << endl;
return 0;
}
两个类的析构函数都没调用,空间没有得到释放
这是因为在类中share1管理了person空间, 而share2有管理了people空间,双方都在等待对方使用完成后释放空间,两个对象相互持有彼此的 shared_ptr,导致了循环引用,从而阻止了析构函数的调用。
即当 People 类的 shared_ptr 指向的 Person 对象超出作用域时,Person 类的 shared_ptr 也
会因为指向 People 对象而保持有效。这导致了一种看似超出作用域但实际上引用计数不为零
的情况,从而阻止了对象的析构。
具体来说,在你的代码中,当 People 类的 shared_ptr 被销毁时,它会尝试释放 Person 对
象。但由于 Person 对象的 shared_ptr 仍然持有对 People 对象的引用,因此 Person 对象并
不会真正释放。同样,当 Person 类的 shared_ptr 被销毁时,它会尝试释放 People 对象,但
由于 People 对象的 shared_ptr 仍然持有对 Person 对象的引用,所以 People 对象也不会真
正释放。
(以上讲人话就是,person要释放空间,但是people还管理person呢,他就认为自己可能还活着,反过来就是people要释放空间,但是person还管理people呢,他也认为自己还活着,所以导致都释放不了空间)
使用 weak_ptr 可以解决这个问题,因为 weak_ptr 不会增加引用计数,它可以观察 shared_ptr 而不影响对象的生命周期。这样,当最后一个 shared_ptr 超出作用域时,对象的引用计数会减少到零,从而正确地触发析构函数。
//weak_ptr弱指针,来解决上述问题
class Person;//提前声明以下Person类防止,报未定义错误(以为我们在people中使用了Person类)
class People {
public:
weak_ptr share1;
~People() {
cout << "释放People的资源" << endl;
}
};
class Person {
public:
weak_ptr share2;
~Person() {
cout << "释放Person的资源" << endl;
}
};
int main()
{
//实例化两个对象
People* people = new People;
Person* person = new Person;
//时share11 share22分别管理对应的空间
shared_ptr share11(people);//管理people
shared_ptr share22(person);//管理person
//接着给对象中的属性赋值,使其属性也管理相应的空间
people->share1 = share22;//使people中的share1属性管理person空间
person->share2 = share11;//使person中的share2属性管理people空间
cout << "引用计数值:" << share11.use_count() << endl;
return 0;
}
空间得到正确释放
切记weak_ptr不会是计数空间增加
weak.expired()
检查 weak 是否已经过期。如果 weak 过期,意味着它不再管理任何资源,即 shared_ptr 已经释放了它所持有的资源。
weak.lock()
weak_ptr 的 lock 方法用于尝试将 weak_ptr 转换为 shared_ptr,如果转换成功,它会返 回一个指向相同资源的 shared_ptr,否则返回一个空指针。
//weak_ptr弱指针基本使用
class People {
public:
~People() {
cout << "释放People的资源" << endl;
}
};
int main()
{
//实例化两个对象
People* people = new People;
//时share11 share22分别管理对应的空间
shared_ptr share11(people);//管理people
weak_ptrweak = share11;//weak_ptr不会是计数空间增加
cout << "引用计数值:" << share11.use_count() << endl; //1
//通过调用 expired 方法来检查 weak 是否已经过期。如果 weak 过期,
// 意味着它不再管理任何资源,即 shared_ptr 已经释放了它所持有的资源。
if (weak.expired())
{
cout << "资源空间已被释放" << endl;
}
//尝试从 weak 中获取一个有效的 shared_ptr。如果 weak 仍然管理着资源(People 对象),则返回一个指向该资源的 shared_ptr。
//如果 weak 不再管理资源(资源已被释放或 weak 已经过期),则返回一个空指针。
shared_ptrshare5=weak.lock();//注意这里计数空间+1
if (share5 == nullptr)
{
cout << "没有管理任何堆空间" << endl;
}
cout << "引用计数值:" << share11.use_count() << endl;//2
return 0;
}
称为“独占式指针”,用unique_ptr去管理空间,那么只允许一个智能指针管理一个空间
确保同一时间只有一个 unique_ptr 可以拥有对对象的所有权。
由于 unique_ptr 拥有独特的所有权,它不支持拷贝语义
//unique_ptr 唯一指针
int main()
{
int* p = new int(50);
unique_ptr unique1(p);
//unique_ptr unique2(unique1);//报错,unique_ptr只允许一个管理
//unique_ptr unique2 = unique1;//报错,unique_ptr只允许一个管理
cout << *unique1 << endl;
cout << "p的地址" << p << endl;
cout << "unique1管理的地址" << unique1 << endl;
return 0;
}