以后要熟悉使用智能指针来避免使用常规指针带来的隐患,所以在这里总结一下关于C++中的智能指针模板类

智能指针是行为类似于指针的类对象,但是他们还有更重要的功能来帮助管理动态内存分配。
他们的一个主要的思想就是:

使用智能指针指向堆中的内存(这和常规指针相同),但是智能指针会在智能指针变量过期销毁时自动将其指向的堆内存释放掉。这样就避免了程序员忘记释放内存而造成的隐患,如内存泄漏。。。
其本质也就是将智能指针模板类的*运算符进行了重载,然后再析构函数中添加了释放对内存的功能。

C++中内置了三种智能指针模板:auto_ptr, unique_ptr, shared_ptr


使用这三种指针的时候要注意:

  • 智能指针类在含有一个参数的构造函数中使用了explicit关键字,因此只能通过显示调用而不是赋值的方式进行初始化。
    1
    2
    shared_ptr<double> pshared = p_reg;  // 不允许,隐式类型转换
    shared_ptr<double> pshared(p_reg); // 允许,这是显式调用构造函数
  • 不能将智能指针指向非对内存中的数据,因为当智能指针过期时,程序会尝试释放非堆内存中的数据,这样是错误的。


为什么有三种智能指针?

关于这三类智能指针的区别主要是围绕一点区分: 如何处理当两个指针共同指向同一个对象
对于常规指针,这没啥问题。但是对于智能指针,由于其自动释放内存的特性,将两个智能指针同时指向一个对象就意味着会将同一块内存释放两次,这当然是不允许的了。

处理上面的问题的方式:

  • 定义复制运算符,进行深复制,再另外开辟出一段内存来存放相同的对象。
  • 建立所有权(ownership)的概念,对于一个对象只能有一个智能指针指向他,只有拥有所有权的智能指针的析构函数能够释放该内存。赋值的时候会转让所有权,老的智能指针将会变成一个空指针,无法使用。这是auto_ptrunique_ptr所采用的策略。
  • 采用引用计数(reference counting),仅当最后一个指针过期时,才调用delete释放内存,这是shared_ptr所采用的策略。


auto_ptrunique_ptr的区别?

采用所有权的策略会有一种隐患就是,当一个指针的所有权转让出去以后便不再有用和数据对象之间的关系,但是程序员可能还会通过它来访问数据。这个时候就相当于使用了一个空指针访问了无效的数据。
auto_ptr指针不会在编译阶段报错通过编译后程序在运行的时候会出现运行阶段错误,而unique_ptr会在编译阶段发现此错误从而禁止程序员这种危险的行为。毕竟编译阶段的错误比潜在的程序崩溃更安全。
由于C++11引入移动构造函数和右值引用,所以可以使用std::move()函数来达到只能指针赋值的效果,如

1
2
3
unique_ptr<string> ps1, ps2;
ps1 = demo("test"); // demo() 返回一个unique_ptr<string>类型的变量
ps2 = move(ps1); // 类似于ps2 = ps1, 但这样编译器是允许的


unique_ptr独有的特点

unique_ptr有指向数组的变体。即使用new []来分配内存和使用delete []释放内存。可以这样

1
std::unique_ptr<double []> pda(new double [10]);

但是其他两种智能指针却无法这样做。

Comments

2016-03-16