目录
本文涉及所有程序
工作原理
使用方法
手动初始化
构造方法初始化
使用实例
use_count();
unique();
reset();
get();
指定删除器
移动语义:std::move();
C++智能指针之shared_ptr
共享式指针:多个指针可以同时指向同一个对象(共享所有权,协同工作),当最后一个指针被销毁或者指向其他对象时,这个对象会被释放;
本文涉及所有程序
00_code.cpp
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class A
{
public:
A()
{
cout << "A" << endl;
}
~A()
{
cout << "~A" << endl;
}
};
void test(shared_ptr<A> pb)
{
cout << "test func" << endl;
}
shared_ptr<A>test1()
{
shared_ptr<A>temp(new A());
return temp;
}
int main(int argc, char const* argv[])
{
// int *p = new int(5);//裸指针
// shared_ptr是类模板
// shared_ptr<int> p = new int(5);//调用类型转换构造函数初始化 explicit不支持
shared_ptr<int> p(new int(5));
cout << *p << endl;
// string *s = new string("hello world");
shared_ptr<string> s(new string("hello world"));
cout << *s << endl;
// int num = 10;
// shared_ptr<int>p2(&num);//报错:智能指针指向栈空间时,会导致释放两次空间或者对象
shared_ptr<A> pa(new A()); //程序运行结束后会自动调用A的析构函数
shared_ptr<A>result = test1();
test(pa);
shared_ptr<A> pa2(pa); // pa2,p同时指向同一个对象或者空间
return 0;
}
01_code.cpp
#include <iostream>
#include <memory>
using namespace std;
int main(int argc, char const *argv[])
{
shared_ptr<int> pi = make_shared<int>(5);
cout << *pi << endl;
shared_ptr<string> ps = make_shared<string>("hello world");
cout << *ps << endl;
//手动初始化和使用make_shared函数模板初始化有什么区别
/**
*推荐使用make_shared进行初始化;开销小,效率高(数据和引用计数存在同一段空间)
*构造函数初始化:数据和引用计数不在同一段空间中,需要间接访问
*/
return 0;
}
02_code.cpp
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
A()
{
cout << "A" << endl;
}
A(int num):m_num(num)
{
cout <<"A int"<<endl;
}
A(const A&& other):m_num(other.m_num)
{
cout <<"A move int"<<endl;
}
~A()
{
cout << "~A" << endl;
}
public:
int m_num;
};
void test(int *ptr)
{
}
void mydelete(A *pa)
{
cout <<"call my delete" <<endl;
delete []pa;
}
int main(int argc, char const *argv[])
{
#if 0
//shared_ptr<A> pa = make_shared<A>(5); //隐式类型转换
//cout<< pa->m_num<<endl;
shared_ptr<A> pb = make_shared<A>();
//cout<< pb->m_num<<endl;
shared_ptr<A>pc(pb);
cout << pb.use_count()<<endl;
if(pb.unique())
{
cout << "pb is unique ptr" << endl;
}
// pb.reset();
// if(pb == nullptr)
// {
// cout << "pb is nullptr" << endl;
// }
//shared_ptr<A>pd(new A(1));
//pb.reset(pd);//reset不支持传入共享指针
pb.reset(new A(6));
cout << pb.use_count() <<endl;
shared_ptr<int>pi(new int(19));
test(pi.get());//void test(int *ptr);
#endif
//指定删除器
//shared_ptr<A>ptr(new A[3],mydelete);
//shared_ptr<A[]>ptr(new A[3]);
// shared_ptr<A>ptr(new A[3],[](A *a)
// {
// delete []a;
// });
//shared_ptr<A>ptr(new A[3],default_delete<A[]>());
shared_ptr<A>ptr(new A(6));
shared_ptr<A>&&rptr = std::move(ptr);
cout << ptr.use_count() <<endl;
cout << rptr.use_count() <<endl;
return 0;
}
03_code.cpp
#include <iostream>
using namespace std;
template <typename T>
class shared_ptr
{
public:
shared_ptr(const shared_ptr<T> &other)
{
this->ptr = other.ptr;
strong_ref++;
}
shared_ptr(const T *t)
{
this->ptr = t;
strong_ref++;
}
T *get()
{
return ptr;
}
~shared_ptr()
{
strong_ref--;
if (strong_ref == 0)
{
delete ptr;
}
}
private:
T *ptr;
static int strong_ref;
};
template <typename T>
int shared_ptr<T>::strong_ref = 0;
int main(int argc, char const *argv[])
{
return 0;
}
工作原理
引用计数增加/减少(原子操作)
- 引用计数增加
- 用一个智能指针初始化另一个智能指针
- 函数传参:传递一个智能指针;
- 函数返回值:返回一个智能指针
- 引用计数减少
- 给智能指针赋予新值,指向一个新对象
- 局部的智能指针离开其作用域
- =nullptr
定义:shared_ptr 智能指针变量名
使用方法
手动初始化
注:shared_ptr p = new int(10); //错误,
shared_ptr是explicit,不可以进行隐式类型转换,只能用构造函数直接初始化;
对于shared_ptr s(new string("hello world"));可以写成shared_ptr s("hello world"),因为"hello world"被当成了const char数组来使用(即字符指针)
std::make_shared函数(C++14提出)
auto p = std::make_shared("hello world");
error:auto p = std::make_shared(new int[]);
功能:在 堆内存中可以动态分配对象,并返回一个shared_ptr;
推荐方法,因为此方法安全,高效;
构造方法初始化
假如初始化化了两个智能智能sp1和sp2,它们指向的是同一个空间,使用构造函数进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量的地址等
make_shared初始化
而使用函数模板进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量等,它们都存储在同一段空间上,因此访问起来更高效
使用实例
1、引用计数增加/减少(原子操作)(以初始化方式一为例)
(1)通过VS2022进行调试,通过打断点,当执行完第一条指令后,引用计数变为1
当执行完第二条指令后,引用计数变为2
程序执行完,引用计数释放
(2)函数传参改变引用计数
初始化第一次,引用计数为1
函数传参,引用计数加1
函数执行完,新参释放,引用计数减1
初始化另一个智能指针,引用计数再次加1
(3)函数返回值为智能指针
函数内的智能指针释放,因此接返回值后,引用计数仍为1
2、make_shared函数进行初始化
对于自定义类型,可以使用隐式类型转换
make_shared也可以对普通变量或对象使用
shared_ptr常规操作
use_count();
功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)
用途:主要用于调试
unique();
判断当前智能指针是否独享(只有它自己指向该对象)某个对象或者空间,独占返回true,否则返回false;
reset();
- reset()无参使用:若该智能指针是独占某个对象,则释放该对象,并将智能指针置nullptr;若不独占,引用计数减1,并将该指针置nullptr;
- reset()代参使用:若该智能指针是独占某个对象,则释放该对象,并将该指针指向新对象;若不独占,则将该指针指向的对象引用计数减1,并将该指针指向新对象;
实例:pb.reset();
pb.reset(new A(6));
get();
功能:获得智能指针中保存的指针(裸指针);主要为了适应C的接口
考虑到有些函数参数是裸指针并不是智能指针,所以需要将智能指针转化为裸指针;
指定删除器
原因:有些情况,默认删除器处理不了(shared_ptr管理动态数组),需要我们自己指定删除器;
对于图中示例,在进行释放的时候,只调用了delete ptr,只释放了一次。而我们希望的是调用delete []ptr将空间全部释放
解决方法1:自定义删除器函数
当默认的删除器不起作用时,需要自己指定删除器:void (*) T(T*);
在shared_ptr的构造函数中函数名作为第二个参数直接传入即可
解决方法2:通过指定类型,直接指定智能指针所执行的是一个数组
解决方法3:lambda表达式
解决方法4:可用标准库模板类default_delete做删除器(函数对象)
移动语义:std::move();
因为对象移动做的不是数据的拷贝,使用右值ptr会认为rptr是个临时变量,因此并不会改变原本引用计数