智能指针

普通指针在内存管理方面非常不方便,new和delete必须成对使用,非常容易出现错误。

智能指针模板类

定义了类似指针的对象,可以将new得到的内存地址赋给这些对象,当这些智能指针过期时,将自动调用其(智能指针对象)的析构函数,这些析构函数将使用delete来释放内存。
因此如果使用智能指针指向new的内存,无需手动释放这些内存,智能指针过期时,内存自动被释放。

有关智能指针的注意事项

1
2
3
auto_ptr<string> ps(new string("this is test string."));
auto_ptr<string> pd;
pd = ps;

如上面的代码所示,如果是常规指针,这显然是错误的,因为pd和ps都指向同一个对象,程序将试图删除同一个对象两次;修正这个错误有以下几个方法:

  1. 定义赋值运算符,执行深复制,拷贝对象,使两个指针指向不同对象(一个对象是另外一个的副本);
  2. 建立所有权(ownership)概念,对于特定对象只能有一个智能指针拥有它,当执行赋值操作时,就转移了对象的所有权,就只有一个智能指针在过期后,其析构函数才能释放对象的内存。auto_ptr和unique_ptr就是采用的这种方法,但是unique_ptr更加严格。
  3. 建立引用计数(reference counting),也就是,将对象内存赋值给一个指针时,计数加1,指针过期时,计数减1。当最后一个指针过期时,才调用析构函数释放内存。这也是shared_ptr的策略。

auto_ptr和unique_ptr的区别、shared_ptr

  • 1
    2
    3
    4
    5
    6
    7
    auto_ptr<string> ps(new string("liangzelang"));
    auto_ptr<string> pd;
    pd = ps; #1

    unique_ptr<string> ps(new string("liangzelang"));
    unique_ptr<string> pd;
    pd = ps; #2

上述代码所示,如果使用auto_ptr,ps转移了对象所有权之后,ps其实指向不确定,如果再使用pd这是一件非常危险的事情,留下了一个危险悬挂指针。unique_ptr在这里,就更加严格,编译器会认为#2是错误的,避免在程序运行阶段奔溃……

  • 当然我们经常会这样使用指针:
    1
    2
    3
    4
    5
    6
    7
    unique_ptr<string> demo(const char * s)
    {
    unique_ptr<string> temp(new string(s));
    return temp;
    }
    unique_ptr<string> ps;
    ps = demo("this is test string");

那么问题来了,这里我们可以知道demo函数会返回一个临时unique_ptr(我们加它XX吧),temp会自动释放空间,在XX将对象所有权交给ps之后,也成危险悬挂指针了???显然不是,在临时unique_ptr归还所有权之后,会自动销毁的,临时嘛………………所以这是完全没有问题安全的。也就是说这种赋值是没有问题的。
总之,试图将一个unique_str赋值给另外一个的时候,如果源unique_ptr是一个临时右值,编译器允许;如果源unique_ptr将存在一段时间,编译器禁止。(有时,你非要这样,也是可以的这就得用到移动语义和右值引用的相关知识了)

  • 另外auto_ptr只能用于new得到的内存空间,如果是数组,使用new[],auto_ptr将不能使用,但是unique_ptr可以使用new或者new[]…

  • shared_ptr,如果真的想多个指针指向同一个对象,就是注意事项第三点方法,采用shared_ptr智能指针。

智能指针的选择

  1. 多个指针指向同一个对象;涉及复制和赋值等操作的STL算法等 使用 shared_ptr;
  2. 不是以上情况,使用unique_ptr;
  3. 在满足2时,可使用auto_ptr(提醒一下,这是C++98的feature,C++11已经抛弃之,懂了吧)

weak_ptr:

避免

1
2
string a("this is a test string");
shared_ptr<string> ps(&a); //No

这些智能指针都只能用于new或者new[]获得的内存,堆。(敲黑板,new出来的内存都在堆,局部变量什么的都在栈上,区别就是……出门google)。