C++日记——Day29:shared_ptr常用操作、计数、自定义删除器等等

shared_ptr引用计数的增加和减少

共享式:引用计数,每个shared_ptr的拷贝都指向相同的内存(对象),只有最后一个指向该对象的shared_ptr指针在不需要在指向该对象时,shared_ptr才会去析构所指向的对象。

 

引用计数的增加

每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象;

auto p6 = make_shared<int>(100);
auto p7(p6); //智能指针定义时初始化,p7和p6指向了相同的对象。


shared_ptr<int> func(shared_ptr<int> &tmp){
    return tmp;
}

auto p8 = func(p7);

在如下情况下,所有指向这个对象的share_ptr引用计数都会增加1;

1、像上边这样用p6来初始化p7这个智能指针;

2、把智能指针当做实参向函数中传递;(如果参数为智能指针的引用,则智能指针的计数不会增加)

3、作为函数的返回值,(如果没有变量来接这个返回的临时智能指针,则临时智能指针生命周期到了以后计数自动减少1)

 

引用计数的减少

p8 = make_shared<int>(200); //p8指向新对象,计数为1,p6、p7指向对象计数恢复为2;
p7 = make_shared<int>(300); //p7指向新对象,计数为1,p6指向对象的计数恢复为1;

1、shared_ptr赋予新值,让该shared_ptr指向一个新对象。

2、局部的shared_ptr离开其作用域。

3、当一个shared_ptr引用计数从1变成0,则它会自动释放自己所管理的对象。

auto p9 = make_shared<int>(100);
auto p10 = make_shared<int>(100);
p9 = p10; //给p9赋值会让p9指向p10所指向的对象,该对象引用计数变成2,而原来p9所指向的 
          //对象引用计数会从1变成0,从而导致p9所指向的对象被释放

 

 

shared_ptr常用操作

use_count():返回多少个智能指针指向某个对象,主要用于调试。

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

 

unique():是否该智能指针独占某个指向的对象,也就是若只有一个智能指针指向某个对象,则unique()返回true。(为空时也不属于独享)

 

reset():恢复(复位/重置)的意思

1、若reset不带参数时

若pi是唯一指向该对象的指针,那么释放pi所指向的对象,并将pi置空

若pi不是唯一指向该对象的指针,那么不释放pi所指向的对象,但指向该对象的引用计数会减少1,同时将pi置空

shared_ptr<int> pi(new int(100));
pi.reset();
if (pi == nullptr)
{
    cout << "pi被置空" << endl;
}
shared_ptr<int> pi(new int(100));
auto pi2(pi);
pi.reset(); //pi所指向的对象的引用计数减少1

2、reset带参数时

若pi是唯一指向该对象的指针,则释放pi指向的对象,让pi指向新对象。

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

 

空指针也可以通过reset重新初始化。

shared_ptr<int> p;
p.reset(new int(10));

 

*解引用:获取p指向的对象。

shared_ptr<int> pi(new int(123));
cout << *pi << endl;

 

get():考虑到有些函数的参数需要的是一个内置裸指针,而不是智能指针。

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

shared_ptr<int> mypi(new int(222));
int *p = mypi.get();
*p = 123;  //此处的p属于智能指针,不要delete

 

swap():交换两个智能指针所指向的对象。

shared_ptr<string> ps1(new string("1111111"));
shared_ptr<string> ps2(new string("2222222"));
std::swap(ps1, ps2); //交换ps1指向222222222
ps1.swap(ps2);    //在交换ps1指向11111111

 

=nullptr

1、将所指向的对象的引用计数减1,若引用计数变为0,则释放智能指针所指向的对象。

2、智能指针将被置空。

 

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

shared_ptr<string> ps1(new string("1111"));
if(ps1)
{
    cout << "ps1指向一个对象" << endl;
}

 

指定删除器以及数据问题

1、指定删除器:一定时机帮我们删除所指向的对象;delete:将delete运算符号作为默认的资源析构方式

我们可以指定自己的删除器取代系统提供的默认删除器。当智能指针需要删除所指向的对象时,编译器就会调用我们自己的删除器来删除该对象

shared_ptr指定删除器的方法比较简单,一般只需要在参数中添加具体的删除器函数名即可

void mydelete(int *p) //这个删除器用于删除整型指针,当智能指针引用计数为0,就会自动调 
                      //用该删除器来删除对象
{
    //写一些日志
    delete p;  //既然你自己提供了删除器你就有义务自己来删除所指向的对象(引用计数为0 
               //时)
}


shared_ptr<int> p(new int(123), mydelete);
shared_ptr<int> p2(p);  //两个引用计数
p2.reset();  //还剩1个引用计数
p.reset();  //因为此时只有1个引用计数,减1后为0,所以要释放所指向的对象,调用mydelete 
            //删除器,同时p置空
shared_ptr<int> p(new int(123), [](int *p){ 
                                            delete p; 
                                          });

有些情况,默认删除器处理不了(比如用shared_ptr管理动态数组),需要我们自己指定删除器;

class A {
public:
    A() {};
    ~A() {};
}

shared_ptr<A> p(new A[10]); //释放时会报内存泄漏异常

shared_ptr<A> p(new A[10], [](A *pa){ 
                                            delete [] pa; 
                                         });

可以用default_delete来做删除器,这个是标准库里的模板类。

class A {
public:
    A() {};
    ~A() {};
}


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

 

C++新规则 还有一种简单写法

shared_ptr<A[]> p(new A[10]);

shared_ptr<int[]> p(new int[10]);
p[0] = 12;
p[1] = 15;

这种情况不需要再指定删除器

 

函数模板封装shared_ptr

template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}


shared_ptr<A> parray = make_shared_array<A>(15);

 

指定删除器额外说明

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

auto lambda1 = [](int *p)
{
    delete p;
};

auto lambda2 = [](int *p)
{
    delete p;
};


shared_ptr<int> p1(new int(100), lambda1);
shared_ptr<int> p1(new int(100), lambda2);
p2 = p1; //p2会先调用lambda2把自己所指向的对象释放,然后指向p1所指向的对象。p1所指向 
         //的对象引用计数为2,整个main执行完成后还会调用lambda1来释放p1,p2共同指向的 
         //对象

类型相同就代表可以放到元素类型为该对象的容器里来;

vector<shared_ptr<int>> pvec{p1, p2};

make_shared是提倡的生成share_ptr方法,但是使用这种方法我们无法指定自己的删除器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章