这里只是介绍智能用法和注意事项,简单介绍其原理,如果想深入了解其实现的同学可以绕道。
- 为什么用智能指针:在编写服务器代码的时候,经常会出现new对象的情况,比如游戏服务器中,一个新玩家上线了,需要new player,放到mapAllPlayer中,直到玩家下线才能删除。但是在玩家下线时,可能调用了map.erese(),忘记delete指针,或者都忘记了,就会造成内存泄漏,也就是内存不断疯长,直到程序崩掉。
- 智能指针的分类:auto_ptr,unique_ptr,shared_ptr.三种,但是auto_ptr因其缺点,已经被弃用,最好不要使用他,后两者是C++11为我们提供的新型智能指针。
- 使用智能指针:
要想使用智能指针,首先先了解下智能指针模板的定义,实际上智能指针就是封装好的三个类。
如上边的代码,每种指针都包含这种类型的构造函数,explicit指构造函数不能隐身转化。也就是说下面的代码是不允许的template<class X> class auto_ptr { public: explicit auto_ptr(X* p = 0); ... }
赋值之后,我们就可以使用类似ps->的用法,使用智能指针了。//正常使用 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); //这是允许的,实际上这是先创建一个智能指针的临时变量, //然后调用赋值运算符,将智能指针赋值给智能指针。
但是一定要避免一种情况,使用临时变量去构造智能指针,如
string str("hello,wordl");
shared_ptr<string> ptr(&str); 这是绝对拒绝的 - 关于几种指针的区别,和注意事项:
这里必须强调一句,智能指针也不是绝对智能的,必须合理使用。
说一种特殊情况:在调用复制构造函数时,常常遇到指针赋值,如下面代码:
这种情况就是不负责任的表现,两个对象的成员变量m_pstr都指向同一个地址,当一个成员变量被销毁会,另一个对象的该成员变量也会变成无效值。导致程序崩溃等。一般的做法是进行深拷贝,也就是在复制构造函数中,去new新变量。而在智能指针中是另一种处理方式:A::A() { m_pstr = new string(); } A::A(const A& a) { m_pstr = a.m_pstr; }
a.建立所有权关系:对一个对象,只有一个智能指针能拥有他,当赋值操作时,转让所有权,旧的指针不在有意义。这是auto_ptr和unique_ptr的策略。
b.创建智能更高的指针,跟踪引用该对象的智能指针数,称为引用计数,赋值时,引用数加一,指针过期时,引用次数减一,最后一个指针过期时,该对象被销毁。
光说概念,可能不懂什么是所有权,什么是引用,下面写一段简单代码,请看完,很有用:
这个代码将pNex = pSrc,对象double(2.0)的所有权给了pNex,但是pSrc变为了悬挂指针,所以cout<<*pSrc,就会崩溃,这也是C++11摒弃了auto_prt的原因,而unique_ptr虽然也是所有权的策略,但是其根本不允许此类的赋值操作。编译器直接报错。但是编译器如果检测到用一个临时变量去赋值unique_ptr时,是允许的:如一个函数:#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; }
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就可以解决崩溃问题。 - 如何选择智能指针:
要根据需求,如果该指针引用的对象要参与赋值操作,需要使用shared_ptr,如果不参与赋值,则可以使用unique_ptr.最好不要使用auto_ptr指针。
还有一点需要注意:如果不是用new出来的指针,三个智能指针都是不能使用的,unique支持new[],其他两个不支持。