shared_ptr

   C++开发处理内存泄漏最有效的办法就是使用智能指针,使用智能指针就不会担心内存泄露的问题了,因为智能指针可以自动删除分配的内存。

智能指针是指向动态分配(堆)对象指针,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每次使用它,内部的引用计数加1,每次析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。

每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候, 内存才会被释放。

可以通过构造函数、赋值函数或者make_shared函数初始化智能指针。

shared_ptr基于”引用计数”模型实现,多个shared_ptr可指向同一个动态对象,并维护一个共享的引用计数器,记录了引用同一对象的shared_ptr实例的数量。当最后一个指向动态对象的shared_ptr销毁时,会自动销毁其所指对象(通过delete操作符)。

shared_ptr的默认能力是管理动态内存,但支持自定义的Deleter以实现个性化的资源释放动作。

最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。当要用make_shared时,必须指定想要创建的对象的类型,定义方式与模板类相同。在函数名之后跟一个尖括号,在其中给出类型。例如,调用make_shared<string>时传递的参数必须与string的某个构造函数相匹配。如果不传递任何参数,对象就会进行值初始化。

通常用auto定义一个对象来保存make_shared的结果。

当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其它shared_ptr指向相同的对象。

可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数(reference count)。无论何时拷贝一个shared_ptr,计数器都会递增。例如,当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。当给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。它是通过另一个特殊的成员函数析构函数(destructor)来完成销毁工作的。类似于构造函数,每个类都有一个析构函数。就像构造函数控制初始化一样,析构函数控制此类型的对象销毁时做什么操作。shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。

如果将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。

使用shared_ptr注意事项:

(1)、不要把一个原生指针给多个shared_ptr管理;

(2)、不要把this指针给shared_ptr;

(3)、不要在函数实参里创建shared_ptr;

(4)、不要不加思考地把指针替换为shared_ptr来防止内存泄漏,shared_ptr并不是万能的,而且使用它们的话也是需要一定的开销的;

(5)、环状的链式结构shared_ptr将会导致内存泄漏(可以结合weak_ptr来解决);

(6)、共享拥有权的对象一般比限定作用域的对象生存更久,从而将导致更高的平均资源使用时间;

(7)、在多线程环境中使用共享指针的代价非常大,这是因为你需要避免关于引用计数的数据竞争;

(8)、共享对象的析构器不会在预期的时间执行;

(9)、不使用相同的内置指针值初始化(或reset)多个智能指针;

(10)、不delete get()返回的指针;

(11)、不使用get()初始化或reset另一个智能指针;

(12)、如果使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变为无效了;

(13)、如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器


 shared_ptr的线程安全性

 

   shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样,即:

  • 一个 shared_ptr 实体可被多个线程同时读取;
  • 两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
  • 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

shared_ptr<int> sp(new int(10));                //一个指向整数的shared_ptr    
assert(sp.unique());                            //现在shared_ptr是指针的唯一持有者     
shared_ptr<int> sp2 = sp;                       //第二个shared_ptr,拷贝构造函数     
assert(sp == sp2 && sp.use_count() == 2);       //两个shared_ptr相等,指向同一个对象,引用计数为2    
*sp2 = 100;                                     //使用解引用操作符修改被指对象    
assert(*sp == 100);                             //另一个shared_ptr也同时被修改     
sp.reset();                                     //停止shared_ptr的使用    
assert(!sp);      

    class shared                                    //一个拥有shared_ptr的类    
    {    
    private:        
        shared_ptr<int> p;                          //shared_ptr成员变量    
    public:        
        shared(shared_ptr<int> p_):p(p_){}          //构造函数初始化shared_ptr        
        void print()                                //输出shared_ptr的引用计数和指向的值        
        {          cout << "count:" << p.use_count()                
        << "v =" <<*p << endl;        
        }    
    };    
    void print_func(shared_ptr<int> p)                //使用shared_ptr作为函数参数    
    {         
        //同样输出shared_ptr的引用计数和指向的值        
        cout << "count:" << p.use_count()            
            << " v=" <<*p << endl;  }    
    int main()    
    {        
        shared_ptr<int> p(new int(100));        
        shared s1(p), s2(p);                        //构造两个自定义类         
        s1.print();        
        s2.print();         
        *p = 20;                                    //修改shared_ptr所指的值        
        print_func(p);        
        s1.print();    
    }   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值