share_ptr常用操作,计数和自定义删除器

举报
无敌清风蓝 发表于 2023/08/31 22:15:37 2023/08/31
【摘要】 share_ptr常用操作,计数和自定义删除器

1. share_ptr常用操作,计数和自定义删除器

—use_count 成员函数,用于返回多少个智能指针指向某个对象,这个成员函数主要是用来做调试,效率不高

shared_ptr<int> myp(new int(100));
int icount = myp.use_count(); //1
shared_ptr<int> myp2(myp);
icount = myp.use_count(); //2
shared_ptr<int> myp3;
myp3 = myp2;
icount = myp.use_count(); //3
icount = myp3.use_count(); //3

—unique成员函数,是否只有一个智能指针指向某个对象,是的话是true,不是则false

share_ptr<int> myp(new int(100));
if(myp.unique())  // 条件成立
{
    //myp独占所指向的对象
    cout << "myp unique ok" << endl;
}
shared_ptr<int> myp2(myp);
if(myp.uninque()) // 条件不成立
{
    cout << "myp unique ok" << endl;
}

—reset成员函数,分三种情况

1.1第一种,若reset不带参数时

如果pi是唯一指向该对象的指针,则释放pi指向的对象,把pi置空

如果pi不是唯一指向该对象的指针,则不释放pi指向的对象,但指向该对象引用计数会减1,同时将pi置空

shared_ptr<int> pi(new int(100));
pi.reset(); //释放pi指向的对象,将pi置空
if(pi == nullptr)
{
    cout << "pi被置空" << endl;
}

继续看如果pi不是唯一指向该对象指针的情形

shared_ptr<int> pi(new int(100));
auto pi2(pi); //pi2现在引用计数为2
pi.reset();//pi被置空,pi2现在引用计数为1

1.2第二种,当reset带参数(一般是一个new出来的指针)时

如果pi是唯一指向该对象的指针,则释放pi指向的对象,让pi指向新内存

如果pi不是唯一指向该对象的指针,则不释放pi指向的对象,但指向该对象引用计数会减1,同时让pi指向新内存

shared_ptr<int> pi(new int(100));
pi.reset(new int(1)); //释放原内存(内容为100的内存),指向新内存(内容为1的内存)

继续看如果pi不是唯一指向该对象指针的情形

shared_ptr<int> pi(new int(100));
auto pi2(pi); //pi2现在引用计数为2
pi.reset(new int(1));//现在pi引用计数为1,上面的pi2引用计数为1
if(pi.unique())  //成立
{
    cout << "pi unique ok" << endl;
}
if(pi2.unique())  //成立
{
    cout << "pi unique ok" << endl;
}

1.3第三种,空指针也可以通过reset来重新初始化

shared_ptr<int> p; //p现在是空指针
p.reset(new int(100)); //释放p指向的对象,让p指向新内存,因为原来p为空,所以就等于啥也没释放

—*解引用

*p:获得p指向的对象

shared_ptr<int> pother(new int(12345));
char outbuf[1024];
sprintf_s(outbuf, sizeof(outbuf), "%d", *pother); //outbuf中的内容就是12345,pother不发生任何变化,引用计数仍然为1

—get成员函数

p.get() :返回p中保存的指针,小心使用,如果智能指针释放了所指向的对象,则返回这个指针所指向的也就变的无效了

shared_ptr<int> myp(new int(100));
int *p = myp.get();
*p = 45;

为什么要有这样一个函数呢,主要是考虑到有些函数的参数需要的是一个内置指针(裸指针),所以需要通过get取得这个内置指针并传递给这样的函数,但要主要不要delete这个get到的指针,否则有不可预料后果

1.4—swap成员函数

用于交换两个智能指针所指向的对象,当然因为是交换,所以引用计数并不发生变化

shared_ptr<string> ps1(new string("I love China1"));
shared_ptr<string> ps2(new string("I love China2"));
std::swap(ps1, ps2); //可以
ps1.swap(ps2) //也可以

1.5—nullptr两个用途

第一,把所指向对象的引用计数减1,若引用计数变为0,则释放智能指针所指向的对象

第二,将智能指针置空

shared_ptr<string> ps1(new string("I love China"));
ps1 = nullptr;

1.6—智能指针的名字作为判断条件

shared_ptr<string> ps1(new string("I love China"));
if(ps1) //成立
{
    cout << "ps1" << endl;
}

1.7—指定删除器和数组问题,分两条

第一是指定删除器

智能指针是怎么自动删除所指向对象呢,delete

同时程序员也可以指定自己的删除其,这样当智能指针删除时,调用的就是程序员自己写的,而不是默认的delete

shared_ptr指定删除其比较简单,一般只需要在参数中添加具体的删除器函数名即可(注意,删除器是一个单形参的函数)

void myDeleter(int * p); // 自己的删除器,删除整型指针用的,当p的引用计数为0,自动调用这个删除器删除对象,释放内存
{
    delete p;
}
在main中
shared_ptr<int> p(new int(12345), myDeleter); //指定删除器
shared_ptr<int> p2(p); //现在两个引用计数指向该对象
p2.reset(); //现在一个引用计数指向该对象,p2为nullptr
p.reset(); //此时只有一个指针指向该对象,所以释放指向的对象,调用自己的删除器myDeleter,同时把p置空

删除器也可以是一个lambda表达式,注意用{}包着的是lambda表达式的组成部分,直接作为一个参数使用

shared_ptr<int> p (new int(12345),[](int * p){
    delete p;
});
p.reset(); //会调用删除器

为什么不用默认的删除器的,因为有些默认的删除器用不了,比如用shared_ptr管理动态数组的时候,就需要指定自己的删除器,默认的删除器不支持数组对象

shared_ptr<int[]> p(new int[10], [](int*p){
    delete[] p;
})
p.reset;

回想之前的内容,如果一个类定义中有析构函数,则程序员必须自己指定删除器,否则会异常

class A
{
    public:
    	A()
        {
            cout << "A()的构造被调用" << endl;
        }
    	~A()
        {
            cout << "~A()的析构被调用" << endl;
        }
}
在main中
shared_ptr<A> pA(new A[10]); //异常,因为系统释放的是pA是使用delete pA而不是使用delete[] pA,所以必须自己写删除器
像这样修改代码,就可以解决
shared_ptr<A> pA(new A[10], [](A*p){
    delete[] p;
});

此外,还可以将default_delete作为删除器,这是一个标准库里的类模板,这个删除器的内部也是通过delete来实现功能的:

shared_ptr<A> pA(new A[10], std::default_delete<A[]>());

很多时候程序员做的工作都是为了保证数组正常释放,其实在定义时候这样写,即使自己不写删除器也可以正常释放内存

shared_ptr<A[]> pA(new A[10]); //<>中加个[]就行了
shared_ptr<int[]> p(new int[10]); //<>中加个[]就行了,并且加了[]后,引用也方便,比如说p[0],p[1],,,,p[9]直接拿来用

所以最好在定义数组时在尖括号<> 中都要加[]

另外也可以自己写个函数模板封装shared_ptr数组

//定义一个函数模板,解决shared_ptr管理动态数组的情形
template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
    return shared_ptr<T>(new T[size], default_delete<T[]>()); //指定了删除器
}
在main里
shared_ptr<int> pinArr = make_shared_array<int>(5);//末尾数字代表数组元素个数

第二是,指定删除器的额外说明

就算这两个shared_ptr指定的删除器不相同,只要他们所指向的对象相同,那么这两个shared_ptr也是属于一个类型的

在main中

auto lambda1 = [](int*p)
{
	delete p;
};
auto lambda2 = [](int*p)
{
	delete p;
};
shared_ptr<int> p1(new int(100), lambda1); //指定lambda1为删除器
shared_ptr<int> p2(new int(200), lambda2); //指定lambda1为删除器
p2 = p1;//p2会先调用lambda2把自己所指向对象释放,然后指向p1所指对象,现在该对象引用计数为2,整个main函数执行完毕之前还会调用lambda1释放p1,p2共同所指向的对象

同一个类型有一个好处是可以放到元素类型为该对象类型的容器里,方便操作

继续在main里面

vector<shared_ptr<int>> pvec{p1, p2};//
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。