zoukankan      html  css  js  c++  java
  • C++11中的四种智能指针

    前言

    C++ STL 提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。其中auto_ptr 是 C++98 提供的解决方案,C+11 已将其摒弃,并提出了 unique_ptr 作为 auto_ptr 替代方案。虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用较新的 unique_ptr,因为 unique_ptr 比 auto_ptr 更加安全。shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针。此外,Boost 库还提出了 boost::scoped_ptr、boost::scoped_array、boost::intrusive_ptr 等智能指针,虽然尚未得到 C++ 标准采纳,但是在开发实践中可以使用。

    C++11智能指针介绍

    智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个shared_ptr引用。该引用计数的内存在堆上分配,当新增一个时引用计数加1,当引用过期时计数减一。只有引用计数为0时,shared_ptr才会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针,并可以通过get函数获得普通指针。

    为什么要使用智能指针

    智能指针的作用是管理一个指针,在使用普通指针时存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

    指针

    普通指针存在的问题

    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

    (C++98的方案,C++11已经抛弃)auto_ptr定义在头文件<memory>中。采用所有权模式。

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

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

    再来一个例子:

    #include <iostream>
    #include <string>
    #include <memory>
    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
    ";
        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
    }
    

    https://www.cnblogs.com/WindSun/p/11444429.html

    https://blog.csdn.net/k346k346/article/details/81478223

  • 相关阅读:
    [转]如何得到Oracle跟踪文件的文件名
    [转]一张图即可说明常规B/S架构
    [原]SQL中获得序列的方法
    [摘]sql走索引,怎么始终有物理读?
    CSS3太强悍了
    [原]简单分析《趣味题》中的SQL
    SendArp获取MAC地址
    nbtstat a ip 获取MAC地址等
    C#定时器的使用
    C#调用WMI关机示例
  • 原文地址:https://www.cnblogs.com/liutongqing/p/12513004.html
Copyright © 2011-2022 走看看