C++深入理解(14)------智能指针(读书笔记)

这里只是介绍智能用法和注意事项,简单介绍其原理,如果想深入了解其实现的同学可以绕道。

  1. 为什么用智能指针:在编写服务器代码的时候,经常会出现new对象的情况,比如游戏服务器中,一个新玩家上线了,需要new player,放到mapAllPlayer中,直到玩家下线才能删除。但是在玩家下线时,可能调用了map.erese(),忘记delete指针,或者都忘记了,就会造成内存泄漏,也就是内存不断疯长,直到程序崩掉。
  2. 智能指针的分类:auto_ptr,unique_ptr,shared_ptr.三种,但是auto_ptr因其缺点,已经被弃用,最好不要使用他,后两者是C++11为我们提供的新型智能指针。
  3. 使用智能指针:
    要想使用智能指针,首先先了解下智能指针模板的定义,实际上智能指针就是封装好的三个类。
    template<class X> class auto_ptr
    {
        public:
            explicit auto_ptr(X* p = 0);
        ...
    }
    如上边的代码,每种指针都包含这种类型的构造函数,explicit指构造函数不能隐身转化。也就是说下面的代码是不允许的
    //正常使用
    class A;        //已定义好的class A、
    std::auto_ptr<A> ps(new A());
    std::shared_ptr<A> ps(new A());
    std::unique_ptr<A> ps(new A());
    
    
    //错误使用:
    shared_ptr<double> pd;
    double* p_reg = new double;
    pd = p_reg;     //这是不允许的,这种实际上是在调用pd的赋值运算符,不允许将非智能指针赋值给智能指针
    
    shared_ptr<double> pshared = p_reg;  //不允许,这是在隐式调用构造函数,explicit不允许隐身调用。
    shared_ptr<double> pshared(p_reg);   //允许的,正常调用构造函数
    
    pd = shared_ptr<double>(p_reg);  //这是允许的,实际上这是先创建一个智能指针的临时变量,
                                     //然后调用赋值运算符,将智能指针赋值给智能指针。
    赋值之后,我们就可以使用类似ps->的用法,使用智能指针了。
    但是一定要避免一种情况,使用临时变量去构造智能指针,如
    string str("hello,wordl");
    shared_ptr<string> ptr(&str); 这是绝对拒绝的
  4. 关于几种指针的区别,和注意事项:
    这里必须强调一句,智能指针也不是绝对智能的,必须合理使用。
    说一种特殊情况:在调用复制构造函数时,常常遇到指针赋值,如下面代码:
    A::A()
    {
        m_pstr = new string();
    }
    
    A::A(const A& a)
    {
        m_pstr = a.m_pstr;
    }
    这种情况就是不负责任的表现,两个对象的成员变量m_pstr都指向同一个地址,当一个成员变量被销毁会,另一个对象的该成员变量也会变成无效值。导致程序崩溃等。一般的做法是进行深拷贝,也就是在复制构造函数中,去new新变量。而在智能指针中是另一种处理方式:
    a.建立所有权关系:对一个对象,只有一个智能指针能拥有他,当赋值操作时,转让所有权,旧的指针不在有意义。这是auto_ptr和unique_ptr的策略。
    b.创建智能更高的指针,跟踪引用该对象的智能指针数,称为引用计数,赋值时,引用数加一,指针过期时,引用次数减一,最后一个指针过期时,该对象被销毁。

    光说概念,可能不懂什么是所有权,什么是引用,下面写一段简单代码,请看完,很有用:
    #include <iostream>
    #include <memory>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	auto_ptr<double> pSrc(new double(2.0));
    	cout << *pSrc << endl;
    
    	auto_ptr<double> pNex = pSrc;
    	cout << *pNex << endl;
    	cout << *pSrc << endl;
    
    	return 1;
    }
    这个代码将pNex = pSrc,对象double(2.0)的所有权给了pNex,但是pSrc变为了悬挂指针,所以cout<<*pSrc,就会崩溃,这也是C++11摒弃了auto_prt的原因,而unique_ptr虽然也是所有权的策略,但是其根本不允许此类的赋值操作。编译器直接报错。但是编译器如果检测到用一个临时变量去赋值unique_ptr时,是允许的:如一个函数:
     
    unique<int> func()
    { 
        unique<int> *p(new int);  
        return p;
    }    
    
    unique_ptr<int> ps = func()   //是允许的,因为其不会造成任何悬挂指针,返回的临时指针及时被销毁了
    
    //使用std::move函数可以将所有权赋给新的一个,但是还是不安全的
    unique_ptr<int> ps1;
    ps1 = std::move(ps);  //此时ps变为悬挂指针,必须赋新值才可以使用
    
    

    如果非要进行赋值操作。那么怎么办,就要用到shared_ptr,他允许赋值,会增加一次引用,将上面代码的auto改为shared就可以解决崩溃问题。
  5. 如何选择智能指针:
    要根据需求,如果该指针引用的对象要参与赋值操作,需要使用shared_ptr,如果不参与赋值,则可以使用unique_ptr.最好不要使用auto_ptr指针。

    还有一点需要注意:如果不是用new出来的指针,三个智能指针都是不能使用的,unique支持new[],其他两个不支持。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值