smartptr讲义

作者: Android开发架构师 | 来源:发表于2019-08-15 17:37 被阅读0次

    为什么会有智能指针?

    在我们现实生活中有这样一个使用场景,每次我们new或者malloc了一块空间,完了必须需要我们手动delete或者free释放这块空间,否则会出现内存泄漏。但是,实际应用中程序未必都会按照顺序执行到释放空间那一步,有时候会在中间部分抛出异常使执行流发生跳转或者发生其他状况,总之代码未按照我们期望的那样去执行,就有可能导致内存泄漏。这时候我们就引入智能指针(自动化管理指针所指向的内存资源的释放),主要是利用RAII机制,RAII它是在面向对象语言中的一种惯用法。资源分配以及初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

    smartptr讲义目录


    1. 智能指针

    Java、python 和 go 等语言中都有垃圾自动回收机制,在对象失去引用的时候自动回收,而且基本上没有指针的概念,而 C++语言不一样,C++充分信任程序员,让程序员自己去分配和管理堆内存,如果管理的不好,就会很容易的发生内存泄漏问题,而 C++11 增加了智能指针(Smart Pointer)。主要分为 shared_ptr、unique_ptr 和 weak_ptr 三种,使用时需要引用头文件<memory>。c++98 中还有 auto_ptr,基本被淘汰了,不推荐使用。而 c++11 中 shared_ptr 和 weak_ptr 都是参考的 boost 库中实现的。

    1.1. 裸指针

    c 语言中最常使用的是 malloc()函数分配内存,free()函数释放内存,而 c++中对应的是new、delete 关键字。malloc()只是分配了内存,而 new 则更进一步,不仅分配了内存,还调用了构造函数进行初始化。使用示例:

    rawptr.cpp //

    #include <iostream>

    int main()

    {

    // malloc 返回值是 void*

    int* argC = (int*)malloc(sizeof(int)); free(argC);

    char*c = (char*)malloc(100); free(c);

    int *age = new int(25); //做了两件事情 1.分配内存 2.初始化 int* height = new int(160);

    delete height; delete age;

    char* arr = new char[100]; delete[] arr;

    /*delete 数组需要使用 delete[],事实上,c++原始支持的数据结构组成的数组不需要[]也可以,但 自定义的数据类型组成的数组必须使用 delete[]*/

    }

    new 和 delete 必须成对出现,有时候是不小心忘记了 delete,有时候则是很难判断在这个地方自己是不是该 delete,这个和资源的生命周期有关,这个资源是属于我这个类管理的还是由另外一个类管理的,如果是我管理的,就由我来 delete,由别人管理的就由别人delete,我就算析构了也不影响该资源的生命周期。例如:

    // 情况 1: 需要自己 delete

    const char* getName() {

    char *valueGroup = new char[1000]; // do something

    return valueGroup;

    }

    // 情况 2: 不需要自己 delete

    const char* getName2() { static char valueGroup[1000]; // do something

    return valueGroup;

    }

    只通过函数签名来看,这两个函数没有什么区别,但是由于实现的不同,有时候需要自己管理内存,有时候不需要,这个时候就需要看文档说明了。这就是使用一个"裸"指针不好的地方。

    一点改进是,如果需要自己管理内存的话,最好显示的将自己的资源传递进去,这样的话,就能知道是该资源确实应该由自己来管理。

    char *getName(char* v, size_t bufferSize) {

    //do something

    return v;

    }

    上面还是小问题,自己小心一点,再仔细看看文档,还是有机会避免这些情况的。但是在c++引入异常的概念之后,程序的控制流就发生了根本性的改变,在写了 delete 的时候还是有可能发生内存泄漏。如下例:

    // memoryleak.cpp

    void badThing(){

    throw 1;// 抛出一个异常

    }

    void test() {

    char* a = new char[1000];

    badThing();

    // do something delete[] a;

    }

    int main(int argc, char* argv[]) { try {

    test();

    }

    catch (int i){

    cout << "error happened " << i << endl;

    }

    return 0;

    }

    可以看到,就算发生了异常,也能够保证析构函数成功执行!这里的例子是这个资源只有一个人使用,我不用了就将它释放掉。但还有种情况,一份资源被很多人共同使用,要等到所有人都不再使用的时候才能释放掉,对于这种问题,就需要对上面的SafeIntPointer 增加一个引用计数,如下:

    可以看到每一次赋值,引用计数都加一,最后每次析构一次后引用计数减一,知道引用计数为0,才真正释放资源。要写出一个正确的管理资源的包装类还是蛮难的,比如上面那个上面例子就不是线程安全的,只能属于一个玩具,在实际工程中简直没法用。而到了C++11,终于提供了一个共享的智能指针解决这个问题。

    1.2.shared_ptr 共享的智能指针

    1.1.1.简介

    shared_pr是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在

    memory文件中,命名空间为std。shared_ptr最初实现于Boost库中,后由C++11引入

    到C++STL.shared_ptr利用引用计数的方式实现了对所管理的对象的所有权的分享,即

    允许多个shared_ptr共同管理同一个对象。像shared_ptr这种智能指针,《Effective

    C++》称之为“引用计数型智能指针”(reference-counting smart pointer,RCSP).

    shared_ptr 是为了解决auto_ptt在对象所有板上的局限性(auto_pr是独占的),在使用引

    用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销:

    (1)shared_ptr对象除了包括一个所拥有对象的指针外,还必须包括一个引用计数代理

    对象的指针;

    (2)时间上的开销主要在初始化和拷贝操作上,*和->操作符重载的开销跟auto_ptr是

    一样;

    (3)开销并不是我们不使用shared_ptr的理由,,永远不要进行不成熟的优化,直到性能

    分析器告诉你这一点。

    因文章过长不能一一的上传了,文章已整理为PDF文档;

    想要获取完整资料的可以私信 关键词【资料】+资料名进行获取。

    相关文章

      网友评论

        本文标题:smartptr讲义

        本文链接:https://www.haomeiwen.com/subject/zpftsctx.html