c++中的四种智能指针

原始指针:通过new建立的*指针

智能指针:通过智能指针关键字(unique_ptr, shared_ptr ,weak_ptr)建立的指针

智能引出的目的是为了解决c++中内存空间管理的问题

1、申请的空间在函数结束时忘记释放,造成内存泄漏;

2、尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。

使用智能指针可以很大程度上的避免这个问题,因为智能指针是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间


普通指针存在的问题:

auto_ptr<string> p1 (new string ("I reigned lonely as a cloud.")); 
auto_ptr<string> p2; 
p2 = p1; //auto_ptr不会报错

如果p1和p2是普通指针,那么两个指针将指向同一个string对象。那么在删除同一个对象两次的时候,会出错。要避免这种问题,方法有多种:
(1)定义陚值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本,缺点是浪费空间,所以智能指针都未采用此方案。
(2)建立所有权(ownership)概念。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权。这就是用于 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更严格。
(3)创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加 1,而指针过期时,计数将减 1,。当减为 0 时才调用 delete。这是 shared_ptr 采用的策略。

>>auto_ptr

auto_ptr<string> p1 (new string ("I reigned lonely as a cloud.")); 
auto_ptr<string> p2; 
p2 = p1; //auto_ptr不会报错

auto_ptr定义在头文件中。采用所有权模式。
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题!

#include 
#include 
#include 
using namespace std;

int main()
{
    auto_ptr<string> films[5] = {
    auto_ptr<string>(new string("Fowl Balls")),
    auto_ptr<string>(new string("Duck Walks")),
    auto_ptr<string>(new string("Chicken Runs")),
    auto_ptr<string>(new string("Turkey Errors")),
    auto_ptr<string>(new string("Goose Eggs"))
    };
    auto_ptr<string> pwin;
    pwin = films[2]; // films[2] loses ownership. 将所有权从films[2]转让给pwin,此时films[2]不再引用该字符串从而变成空指针

    cout << "The nominees for best avian baseballl film are\n";
    for (int i = 0; i < 5; ++i)
    {
        cout << *films[i] << endl;
    }
    cout << "The winner is " << *pwin << endl;
    return 0;
}

编译时程序不会出错,但是运行时程序崩溃。因为films[2] 已经是空指针,*films[2]访问空指针时程序会崩溃。但这里如果把 auto_ptr 换成 shared_ptr 或 unique_ptr 后,程序就不会崩溃,原因如下:

使用 shared_ptr 时运行正常,因为 shared_ptr 采用引用计数,pwin 和films[2] 都指向同一块内存,在释放空间时因为事先要判断引用计数值的大小,因此不会出现多次删除一个对象的错误。

使用 unique_ptr 时编译出错,与 auto_ptr 一样unique_ptr 也采用所有权模型,但在使用 unique_ptr 时,程序不会等到运行阶段崩溃,而在编译下述代码行出现错误:

pwin = films[2];  //films[2] loses ownership

提示你发现潜在的内存错误。这就是为何要摒弃 auto_ptr 的原因,一句话总结就是:避免因潜在的内存问题导致程序崩溃。

从上面可见,unique_ptr 比 auto_ptr 更加安全,因为 auto_ptr 有拷贝语义,拷贝后原对象变得无效,再次访问原对象时会导致程序崩溃;unique_ptr 则禁止了拷贝语义,但提供了移动语义,即可以使用std::move() 进行控制权限的转移,如下代码所示:

unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt);	//编译出错,已禁止拷贝
unique_ptr<string> upt1=upt;	//编译出错,已禁止拷贝
unique_ptr<string> upt1=std::move(upt);  //控制权限转移

auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt);	//编译通过
auto_ptr<string> apt1=apt;	//编译通过

这里要注意,在使用std::move将unique_ptr的控制权限转移后,不能够再通过unique_ptr来访问和控制资源了,否则同样会出现程序崩溃。我们可以在使用unique_ptr访问资源前,使用成员函数get()进行判空操作。

unique_ptr<string> upt1=std::move(upt); //控制权限转移
if(upt.get()!=nullptr)		           //判空操作更安全
{
	//do something
}

此部分解释摘自:C++11中的四种智能指针


>>shared_ptr

采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。 大小为两个指针;一个用于对象,另一个用于包含引用计数的共享控制块。 头文件:

>>unique_ptr

只允许基础指针的一个所有者,即保证同一时间内只有一个智能指针可以指向该对象。 与 boost::scoped_ptr 比较。 unique_ptr 小巧高效;大小等同于一个指针且支持 rvalue 引用,从而可实现快速插入和对 STL 集合的检索。 头文件:

它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。

在这里插入代码片

>>weak_ptr

share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

参考文献二—博客园
参考文献三—CSDN

你可能感兴趣的:(c++学习)