芝士wa
2024.4.8
参考视频链接
概述
C++的指针包括两种:
- 原始指针
- 智能指针:unique_ptr,shared_ptr,weak_ptr
智能指针是原始指针的封装,其优点是会自动分配内存,不用担心潜在的内存泄漏。
各种指针中,最常用的是裸指针,其次是unique_ptr和shared_ptr
weak_ptr是shared_ptr的一个补充,应用场景较少
智能指针只解决一部分问题,即独占/共享所有权指针的释放、传输
auto_ptr被C++抛弃的主要原因
-
复制或者赋值都会改变资源的所有权
auto_ptr<string> p1(new string("a")); auto_ptr<string> p2(new string("b")); p1 = p2;
当p2赋值给p1后,首先p1会将自己原先托管的指针释放掉,然后接受p2所托管的指针,p2指向NULL。
-
在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值
-
不支持对象数组的内存管理
auto_ptr<int[]> array(new int[5]);//不能这样定义
unique_ptr
- 在任何时刻,只能有一个指针管理内存,两个unique_ptr不能指向同一个资源
- 当指针超过作用域时,内存将自动释放
- 该类型指针不可copy,只可以move
- 在容器中保存指针是安全的
三种创建方式
- 通过已有裸指针创建
- 通过new来创建
- 通过std::make_unique创建(推荐)
创建一个空的unique_ptr对象:
std::unique_ptr<int> ptr;//空指针
使用原始指针创建,将一个new操作符返回的指针传递给unique_ptr的构造函数,
std::unique_ptr<class> ptr(new class());
std::unique_ptr<class> ptr = std::unique_ptr<class>(new class());
//以上两句代码是等价的,第一句使用了直接初始化,第二句创建了一个临时对象,然后将该临时对象复制给ptr
不能通过赋值的方法创建对象,下面的这句是错误的
std::unique_ptr<class> ptr = new class();//error
C++14引入了std::make_unique创建unique_ptr对象,可以使用std:::make_unique()函数创建
std::unique_ptr<class> ptr = std::make_unique<class>();
推荐使用make_unique方法。
unique_ptr的操作
- 使用get()函数获取管理对象的指针
class *p1 = ptr.get();//get()会返回地址
- 重置对象
在unique_ptr对象上调用reset()函数将重置它,释放原始指针并置空
ptr.reset();
检查unique_ptr对象是否为空
// 方法1
if(!ptr)
std::cout<<"ptr is empty"<<std::endl;
// 方法2
if(ptr == nullptr)
std::cout<<"ptr is empty"<<std::endl;
无法进行复制构造和赋值操作
int main()
{
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2(pInt); // 报错
unique_ptr<int> pInt3 = pInt; // 报错
}
- 使用move函数移交所有权
std::unique_ptr<class> p1 = std::make_unique<class>();
std::unique_ptr<class> p2 = std::make_unique<class>();
p2 = p1.move();//此时p1置空,p2获得p1原先指向对象的所有权。
-
unique_ptr可以作为函数返回值
-
释放关联的原始指针
在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。
shared_ptr
采用引用计数的办法,可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它。这就是shared_ptr采用的策略。
创建方法
- 默认构造函数
std::shared_ptr<T> ptr;//空指针
- 使用裸指针进行构造
std::shared_ptr<T> ptr(new T);
- 拷贝构造
std::shared_ptr<T> ptr1;
std::shared_ptr<T> ptr2(ptr1);//两个指针指向相同的资源,共同管理其生命周期
- 使用自定义删除器进行构造
std::shared_ptr<T> ptr(new T, deleter);//删除器是一个函数或函数对象,用于在 std::shared_ptr 对象释放资源时执行自定义的清理操作。
- 使用自定义删除器和分配器进行构造
std::shared_ptr<T> ptr(new T, deleter, allocator);//配器用于在 std::shared_ptr 对象需要重新分配内存时进行内存分配操作。
- 推荐使用make_shared方法
std::shared_ptr<int> ptr = std::make_shared<int>(42);
通过 std::make_shared 创建的 std::shared_ptr 具有以下优点:
- 内存分配是一次性完成的,同时包含对象和控制块,减少了内存开销。
- 更安全,避免了裸指针的使用,因为 std::make_shared 会处理资源的释放。
- 更高效,因为 std::make_shared 可以对内存进行更优化的管理。
shared_ptr操作
-
引用计数
调用use_count函数可以获得当前托管指针的引用计数 -
管理动态数组
std::shared_ptr<int[]> ptr(new int[5]);
- 主动释放对象
shared_ptrr<int> up1(new int(10));
up1 = nullptr ; // int(10) 的引用计数减1,计数归零内存释放
- 重置
p.reset() ; //将p重置为空指针,所管理对象引用计数减1
p.reset(p1); //将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
p.reset(p1,d); //将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器
- 交换
std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
weak_ptr
- weak_ptr指针通常不单独使用,只能和 shared_ptr 类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
- weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
构造方法
shared_ptr<class T> sp;
weak_ptr wp; //定义为空的弱指针
weak_ptr wp2(sp);//使用共享指针构造
wp2 = sp; //允许共享指针赋值给弱指针
操作方法
- 弱指针也可以获得引用计数,但不能改变引用计数
wp.use_count();
-
弱指针不支持*和->对指针的访问
-
可以转换成共享指针lock()
lock()函数:如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。
shared_ptr<class T> sp_T;
sp_T = wp.lock();
sp_T = nullptr;
-
重置
reset()函数:将当前 weak_ptr 指针置为空指针。 -
expired()
判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)
死锁问题
如果有两个shared_ptr相互引用,那么这两个shared_ptr指针的引用计数永远不会下降为0,资源永远不会释放。
举例:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout << "A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout << "B delete\n";
}
};
void fun() {
shared_ptr<B> pb(new B());
cout << "pb.use_count " << pb.use_count() << endl;//1
shared_ptr<A> pa(new A());
cout << "pa.use_count " << pa.use_count() << endl;//1
pb->pa_ = pa;
cout << "pb.use_count " << pb.use_count() << endl;//1
cout << "pa.use_count " << pa.use_count() << endl;//2
pa->pb_ = pb;
//由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1
cout << "pb.use_count " << pb.use_count() << endl;//2
cout << "pa.use_count " << pa.use_count() << endl;//2
}//程序结束时,没有调用A和B的析构函数
int main()
{
fun();
system("pause");
return 0;
}
为了解决死锁问题,采用weak_ptr,把A中的shared_ptr pb_ 改为weak_ptr pb_weak,传递时不会增加计数,最终能使资源正常释放。
标签:std,p1,智能,shared,unique,ptr,指针 From: https://www.cnblogs.com/cheese-wa/p/18121779