• 智能指针


    前奏:

    Garbage Collection 技术一直颇受注目,并且在 Java 中已经发展成熟,成为内
    存管理的一大利器,但它在 C++ 语言中的发展却不顺利,C++ 为了追求运行速度,20 年来
    态度坚决地将其排除在标准之外。

    为了稍许平复因为没有 Garbage Collection 而引发的 C++ 程序员的怨气,C++
    对 Smart Pointer 技术采取了不同的态度。

    首先,了解一下智能指针,

    该方法使用一个指针类来代表对资源的管理逻辑,并将指向资源的句柄(指针
    或引用)通过构造函数传递给该类。当离开当前范围(scope)时,该对象的析构函数一定会
    被调用,所以嵌在析构函数中的资源回收的代码也总是会被执行。这种方法的好处在于,由
    于将资源回收的逻辑通过特定的类从原代码中剥离出来,自动正确地销毁动态分配的对象,
    这会让思路变得更加清晰,同时确保内存不发生泄露。

    它的一种通用实现技术是使用引用计数(Reference Count) 。引用计数智能指针,是一
    种生命期受管的对象,其内部有一个引用计数器。当内部引用计数为零时,这些对象会自动
    销毁自身的智能指针类。每次创建类的新对象时,会初始化指针并将引用计数置为 1 ;当对
    象作为另一对象的副本而创建时,它会调用拷贝构造函数拷贝指针并增加与之相应的引用计
    数 ;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数 ;如果引用计数
    减至 0,则删除对象,并增加右操作数所指对象的引用计数 ;调用析构函数时,构造函数减
    少引用计数,直到计数为 0,释放对象空间。

    其次,auto_ptr包含在#include <memory>

    auto_ptr 可以指向一个以 new 建立的对象,当 auto_ptr 的生命周期结束时,其所指向的
    对象之资源也会被自动释放,且不必显式地调用 delete,而对象指针的操作依旧如故。例如:
    class A
    {
    public:
    A(){}
    ~A(){}
    void Hello()

    {
    std::cout<<"Hello Smart Pointer";
    }
    };
    int main()
    {
    std::auto_ptr<A> pA(new A());
    pA->Hello();
    return 0;
    }
    当然,也可以建立一个未指向任何对象的 auto_prt,例如:
    std::auto_ptr<int> iPtr;
    它就像空指针,未指向任何对象,所以也就不能进行操作,但是可以通过 get() 函数来
    判断它是否指向对象的地址
    if(iPtr.get() == 0) // 不指向任何对象
    {
    iPtr.reset(new int(2011)); // 指向一个对象
    }
    auto_ptr 还可以使用另一个 auto_ptr 来建立,但是需要十分小心的是,这会造成所有权
    的转移,例如:
    auto_ptr< string> sPtr1 (new string("Smart Pointer"));
    auto_ptr< string> sPtr2 (sPtr1);
    if( !sPtr1->empty() )
    cout<<*sPtr1<< endl;
    当使用 sPtr1 来建立 sPtr2 时,sPtr1 不再对所指向对象的资源释放负责,而是将接力
    棒传递到了 sPtr2 的手里,sPtr1 丧失了使用 string 类成员函数的权利,所以在判断 sPtr1-
    >empty() 时程序会崩溃。
    auto_ptr 的资源维护动作是以 inline 的方式来完成的,在编译时代码会被扩展开来,所
    以使用它并不会牺牲效率。虽然 auto_ptr 指针是一个 RAII (Resource Acquisition In Initialization)对象,能够给我们带来很多便利,
    但是它的缺点同样不可小觑:

    auto_ptr 对象不可作为 STL 容器的元素,所以二者带来的便利不能同时拥有。这一重
    大缺陷让 STL 的忠实拥趸们愤怒不已。
    auto_ptr 缺少对动态配置而来的数组的支持,如果用它来管理这些数组,结果是可怕
    的、不可预期的。
    auto_ptr 在被复制的时候会发生所有权转移

    就在 2011 年的 9 月刚刚获得通过的 C++ 新标
    准 C++ 11 中废弃了 auto_ptr 指针,取而代之的是两个新的指针类:shared_ptr 和 unique_ptr
    shared_ptr 只是单纯的引用计数指针,unique_ptr 是用来取代 auto_ptr 的。unique_ptr 提供了
    auto_ptr 的大部分特性,唯一的例外是 auto_ptr 的不安全、隐性的左值搬移 ;而 unique_ptr
    可以存放在 C++0x 提出的那些能察觉搬移动作的容器之中。

    在 Boost 中的智能指针共有五种 :scoped_ptr、scoped_array、shared_ptr、shared_array、
    weak_ptr,其中最有用的就是 shared_ptr,它采取了引用计数,并且是线程安全的,同时支
    持扩展,推荐在大多数情况下使用。

    boost::shared_ptr 支持 STL 容器:
    typedef boost::shared_ptr<string> CStringPtr;
    std::vector< CStringPtr > strVec;
    strVec.push_back( CStringPtr(new string("Hello")) );

    当 vector 被销毁时,其元素 — 智能指针对象才会被销毁,除非这个对象被其他的智能
    指针引用,如下面的代码片段所示:
    typedef boost::shared_ptr<string> CStringPtr;
    std::vector< CStringPtr > strVec;
    strVec.push_back( CStringPtr(new string("Hello")) );
    strVec.push_back( CStringPtr(new string("Smart")) );
    strVec.push_back( CStringPtr(new string("Pointer")) );
    CStringPtr strPtr = strVec[0];
    strVec.clear(); //strVec 清空,但是保留了 strPtr 引用的 strVec[0]
    cout<<*strPtr<<endl; // strVec[0] 依然有效

    Boost 智能指针同样支持数组,boost::scoped_array 和 boost::shared_array 对象指向的是
    动态配置的数组。
    Boost 的智能指针虽然增强了安全性,处理了潜在的危险,但是我们在使用时还是应该
    遵守一定的规则,以确保代码更加鲁棒。

    规则 1:Smart_ptr<T> 不同于 T*
    Smart_ptr<T> 的真实身份其实是一个对象,一个管理动态配置对象的对象,而 T* 是指
    向 T 类型对象的一个指针,所以不能盲目地将一个 T* 和一个智能指针类型 Smart_ptr<T> 相
    互转换。
    在创建一个智能指针的时候需要明确写出 Smart_ptr<T> tPtr<new T>。
    禁止将 T* 赋值给一个智能指针。

    不能采用 tPtr = NULL 的方式将 tPtr 置空,应该使用智能指针类的成员函数。

    规则 2:不要使用临时的 share_ptr 对象

    class A;
    bool IsAllReady();
    void ProcessObject(boost::shared_ptr< A> pA, bool isReady);
    ProcessObject(boost::shared_ptr(new A), IsAllReady());
    调用 ProcessObject 函数之前,C++ 编译器必须完成三件事:
    (1) 执行 "new A"。
    (2) 调用 boost::shared_ptr 的构造函数。
    (3) 调用函数 IsAllReady()。
    因为函数参数求值顺序的不确定性,如果调用 IsAllReady() 发生在另外两个过程中间,
    而它又正好出现了异常,那么 new A 得到的内存返回的指针就会丢失,进而发生内存泄露,
    因为返回的指针没有被存入我们期望能阻止资源泄漏的 boost::shared_ptr 上。避免出现这种
    问题的方式就是不要使用临时的 share_ptr 对象,改用一个局部变量来实现在一个独立的语
    句中将通过 new 创建出来的对象存入智能指针中:

    boost::shared_ptr<A> pA(new A)
    ProcessObject(pA, IsAllReady());
    如果疏忽了这一点,当异常发生时,可能会引起微妙的资源泄漏。

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     5 #include <vector>
     6 #include <time.h>
     7 #include <assert.h>
     8 #include <memory>
     9 #include <signal.h>
    10 using namespace std;
    11 
    12 int main(){
    13     shared_ptr<int> p(new int(4));
    14     shared_ptr<int> p2 = make_shared<int>(4);
    15     cout << *p << " ";
    16     unique_ptr<int> p3(new int(4));
    17     cout << *p3 << " ";
    18     return 0;
    19 }
  • 相关阅读:
    linux(fedora) 下dvwa 建筑环境
    【ThinkingInC++】2、输入和输出流
    Caused by: java.lang.ClassNotFoundException: javax.transaction.TransactionManager
    SpringMVC注释启用
    XML wsdl soap xslt xsl ide
    一个解析RTSP 的URL函数
    PHP:header()函数
    jquery实现鼠标焦点十字效果
    拼出漂亮的表格
    Oracle中如何插入特殊字符:& 和 ' (多种解决方案)
  • 原文地址:https://www.cnblogs.com/guxuanqing/p/4805054.html
Copyright © 2020-2023  润新知