智能指针

智能指针诞生的原因:

在动态内存管理过程,要注意释放动态内存的正确时间,如果不释放的话,会造成内存泄露;但是释放时若还有指针引用内存的话,会产生引用非法内存的指针。而智能指针就是为了解决这一难点而诞生的,就如它的名字一样,如果你使用智能指针的话,它就会智能的在合适的时机释放掉内存。智能指针包括shared_ptr,unique_ptr和weak_ptr.

shared_ptr和unique_ptr支持的操作

shared_ptr和unique_ptr支持的操作;

shared_ptr<T>sp; //T为参数类型,默认为空指针
unique_ptr<T>up;
*p;//解引用
p->get();//返回智能指针保存的指针
swap(p,q);//交换p和q种的指针
//构造一个shared_ptr
make_shared<T>(arg);//返回一个shared_ptr对象,并用arg来初始化它
shared_ptr<T>p(q);//用q来初始化p
p.use_count();//返回p种的计数。

智能指针的实现机制:

我们可以认为每个智能指针都有绑定一个计数器,通常称其为引用计数。无论我们何时拷贝一个智能指针,计数器都会递增,例如

①用一个shared_ptr初始化另一个shared_ptr

shared_ptr<T>p(q);//q中的计数器会加一

②将智能指针作为参数传递

int func(std::shared_ptr<int>p){
    return p->use_count(); 
}
int mian(){
    std::shared_ptr<int>p = std::make_shared<int>(10);
    std::cout<<func(p);//输出的结果为2
}

③作为参数的返回值:

shared_ptr<T> func();

shared_ptr和new结合使用:

shared_ptr的构造函数时explict修饰的,因此我们不能将一个指针隐式转换为智能指针。

shared_ptr<int>p = new int(10);//错误,不能将内置指针转换为智能指针
shared_ptr<int>p(new int(10));//ok

另外shared_ptr还支持reset操作来改变智能指针绑定的指针。

p.reset();//p取消与指针的绑定,变成默认的nullptr;
p.reset(new int(20));//p与新的匿名地址绑定。

unique_ptr

unique_ptr与shared_ptr不同的是,它不能够拷贝,也就是说它的计数器是能小于等于1.当unique_ptr被销毁的时候,它指向的对象也将被销毁。

unique不支持拷贝和赋值:

std::unique_ptr<int>p(new int(10));//ok,直接初始化。
std::unique_ptr<int>q(p);//错误,不能拷贝
q = p;//错误,不能赋值

unique支持reset和release操作:

reset的操作和shared_ptr的reset操作相同,release操作会返回当前智能指针保存的对象并将智能指针置为空。通过这两个操作可以进行unique_ptr指向对象的转移。

p.reset(q.release());//将q保存的对象转移给p。

智能指针自定义删除器

shared_ptr和unique_ptr默认为用delete删除对象,也可以自定义删除器

shared_ptr的自动删除器定义如下:

假设现在有class 为connection,删除函数为disconnect(connection *con);那么自动删除定义删除器为:

std::shared_ptr<connection>p(new connection(),disconnect);
//第二个参数为自定义的删除器函数。

unique_ptr的自动删除器要在参数列表指明删除器的函数指针类型,这里用decltype函数自动推导。

std::unique_ptr<connection,decltype(disconnect)*>p
(new connection(10),disconnect);

weak_ptr

weak_ptr是一种不控制对象生长周期的智能指针,它指向一个shared_ptr指向的对象,将weak_ptr绑定到一个shared_ptr保存的对象上,也不会增加shared_ptr的计数器。一旦最后一个指向对象的shared_ptr被销毁,对象将被释放,即使还有weak_ptr绑定这个对象。

std::shared_ptr<int>p(new int(10));
std::weak_ptr<int>wp(p);//wp弱共享p,p的引用计数不会改变

由于对象不存在,所以不能用wp直接访问对象,而是用成员函数lock来检查指向的对象是否存在,lock就返回一个指向的shared_ptr.与其它shared_ptr不同,只要此shared_ptr存在,它的底层对象就一直存在。

if(shared_ptr<int>np = wp.lock())//只有np不为空时才能进入if
{
    //在if中使用共享对象np是安全的,np和p共享对象。
}

weak_ptr可以解决shared_ptr的循环引用:

class B;
class A{
public:
    A(){
        std::cout<<"A construction"<<std::endl;
    }
    ~A(){
        std::cout<<"A destruction"<<std::endl;
    }
    std::shared_ptr<B>pb;
};
class B{
public:
    B(){
        std::cout<<"B construction"<<std::endl;
    }
    ~B(){
        std::cout<<"B destruction"<<std::endl;
    }
    std::shared_ptr<A>pa;
};
int main(){
    std::shared_ptr<A>a(new A());
    std::shared_ptr<B>b(new B());
    a->pb = b;
    b->pa = a;
}

上面的代码的输出是什么呢?

A construction
B construction

没错,可以很显然的看到A和B在主函数结束后并没有被销毁,因为A和B互相持有对方的引用计数,两者的引用计数都不会较小到0,因此内存也不会被释放。

而将代码修改到如下后:


class B;
class A{
public:
    A(){
        std::cout<<"A construction"<<std::endl;
    }
    ~A(){
        std::cout<<"A destruction"<<std::endl;
    }
    std::weak_ptr<B>pb;
};
class B{
public:
    B(){
        std::cout<<"B construction"<<std::endl;
    }
    ~B(){
        std::cout<<"B destruction"<<std::endl;
    }
    std::weak_ptr<A>pa;
};
int main(){
    std::shared_ptr<A>a(new A());
    std::shared_ptr<B>b(new B());
    a->pb = b;
    b->pa = a;
}

输入为

A construction
B construction
B destruction
A destruction

前面我们说过weak_ptr是弱共享,不会增加shared_ptr的计数引用,因此A和B只有本身一个计数引用,当自身离开作用域后,计数器归0,释放内存。

posted @ 2021-01-25 21:39  Emiria  阅读(140)  评论(0编辑  收藏  举报