zoukankan      html  css  js  c++  java
  • 技术分享会之——智能指针

    因为之前也仅仅是了解智能指针,要我说预计仅仅能说个它是干什么的。用不了几分钟。

    昨天花了一天时间各种百度,算是对智能指针有了一点了解。这篇文章基本就是这次分享会的PPT的copy。没有底层的东西,多是概念。

    我认为理解智能指针须要了解它发展的三个过程:起因,经过,结果。

    这篇文章主要讲述的是起因。经过和结果等以后工作了。实际接触了再说吧。


    起因:

    1.为什么须要智能指针

    我们先看两个样例

    一:内存泄露

    <pre name="code" class="cpp">//内存泄露
    #include <stdio.h>
    #include <iostream>
    using namespace std;
    class A
    {
    public:
        A(char *cstr);
        A(const A &ca);
        A();
        ~A();
        A operator=(const A &ca);
    private:
        char *str;
    };                        
    A::A(char *cstr)            
    {
        str = new char[20];
        strcpy(str, cstr);
    }
    A::A(const A &ca)           
    {
        str = new char[20];
        strcpy(str, ca.str);
    }
    A A::operator=(const A &ca)   
    {
        str = new char[20];      
        strcpy(str, ca.str);
        return str;
    }
    A::A() : str(new char[20])
    {
        strcpy(str, "Welcome!");
    }
    A::~A()
    {
        delete[] str;
        str = NULL;
    }
    int main(void)
    {
        A a("Hello world!");
        A b(a);
        A c;
        c = b;
    	
        return 0;
    }
    
    
    A A::operator=(constA &ca)   
    {
        str =new char[20];      
        strcpy(str, ca.str);
        return str;
    }
    •罪魁祸首就是这段函数,当c=b的时候,就已经发生了内存泄露的隐患。
    •原因:
    –Ac 这句话调用了构造函数,已经在内存空间new了一块空间。

    –c=b 这句调用了操作符重载函数,相同new了一块空间,这样上一次new的空间我们就找不到了。
    –即使在析构的时候delet了第二次申请的空间,可是依旧无法避免内存泄露的问题。

    解决的方法:

    一:
    A::operator=(constA &ca)
    {
    strcpy(str, ca.str);
    }

    可是前提是我们已经申请了空间。
    二:
    A::operator=(const A &ca)   
    {
    delete[] str;
    str = new char[20];
    strcpy(str, ca.str);
    }
    这是个比較危急的方法,假设str没有被初始化或者已经被delet过了,程序就会发生致命错误。
    三:
    A::operator=(const A &ca) 
    {
    if(str == NULL)
    {          
    str = new char[20];
    }     
    strcpy(str, ca.str);
    }
    这是最好的方法,能够在非常多源代码中发现都是用了这样的方法。

    可是须要注意的是str在没有被分配前必须初始化为NULL,由于在未分配和初始化NULL的时候,str不一定是个空指针。还可能是一个野指针。

    二:悬垂指针

    #include <stdio.h>
    #include <iostream>
    #include <windows.h>
    using namespace std;
    
    
    
    int *p=NULL;
    void fun()
    {
    	int i=10;
    	p=&i;
    }
    
    int main()
    {
    	fun();
    	cout<<"*p= "<<*p<<endl;//输出p=10
    	Sleep(1000);
    	cout<<"*p= "<<*p<<endl;//输出p=0
    	return 0;
    }
    两次输出的值不一样,由于程序发生了悬垂指针的错误。

    详细和操作系统的堆栈管理方式有关,感兴趣能够去百度~

    在调用fun()函数的时候。指针P指向了这个模块内的一个整型,当这个模块刚刚返回,这个整型还是存在的。可是过了一会(详细多久还要看详细情况),它被系统释放掉了,那么P所指向的内容就不是我们能控制的了。这就是悬垂指针。

    悬垂指针的定义:指向以前存在的对象,但该对象已经不再存在了。此类指针称为悬垂指针。结果没有定义。往往导致程序错误。并且难以检測。

    以上能够说是智能指针的起因了吧,我做过一些小项目,所以能深深体会到智能指针的必要性。有时候编敲代码的时候总会new一些东西,要么忘了销毁,要么搞不清楚在哪里销毁,这或许和我经验不足有关,可是难保一些大神们不会犯这样的错。

    2.为什么不自建“智能”指针

    这里的为什么的主语-“我”特指像我这种新手。

    既然new和delete要成对的出现,那我们为什么不自己封装一个类。创建的时候new。析构的时候delete呢?之所以特指。是由于新手考虑问题都比較片面,这样除了相同会造成上面的内存泄露的问题,还会造成其它错误。

    class intptr 
    { 
    private: 
     int* m_p; 
    public: 
     intptr(int* p){ m_p = p; } 
     ~intptr(){ delete m_p; } 
     int& operator*(){ return *m_p; } 
    };
    这就是上面想法的一个样例,当我们这样调用的时候不会出现故障:
    somefunction()
    {
     intptr pi(new int); 
     *pi = 10; 
     int a = *pi; 
    }
    可是假设换种方式:
    void somefunction()
    {
     intptr pt1(new int);
     intptr pt2(new int);
     *pt1 = 10;
     pt2 = pt1;
    }
    问题就出现了,pt2指向了pt1所指向的内容。那么pt2原来所指向的内容我们就无法获取和销毁了,这也是一种内存泄露。

    另外在程序块结束后,这块内存将要被销毁两次,这明显是不符合逻辑的。



    经过:

    1.引用计数

    因为以上问题,我们又引入了引用计数这个概念。
    在引用计数中,每个对象负责维护对象全部引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时。引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。

    2.几种智能指针的概述

    这里仅仅是概述。不说使用方法和分析源代码
    智能指针有非常多:
    std::auto_ptr、
    boost::scoped_ptr、
    boost::shared_ptr、
    boost::scoped_array、
    boost::shared_array、
    boost::weak_ptr、
    boost::intrusive_ptr

    auto_ptr

    缺点OR特性:
    不能作为STL的成员(C++标准明白禁止这样做。否则可能会碰到不可预见的结果)
    不能共享全部权(不一定是缺点,有它一定的应用)
    不能指向数组
    不能通过赋值操作来初始化
    std::auto_ptr<int> p(new int(42)); //OK
    std::auto_ptr<int> p = new int(42); //ERROR

    scoped_ptr

    •boost::scoped_ptr的实现和std::auto_ptr很类似,都是利用了一个栈上的对象去管理一个堆上的对象,从而使得堆上的对象随着栈上的对象销毁时自己主动删除。

    不同的是,boost::scoped_ptr有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr指针是不能转换其全部权的。


    •缺点OR特性:
    –不能转换全部权
    boost::scoped_ptr所管理的对象生命周期只局限于一个区间(该指针所在的"{}"之间)。无法传到区间之外,这就意味着boost::scoped_ptr对象是不能作为函数的返回值的(std::auto_ptr能够)。

    –不能共享全部权
    这点和std::auto_ptr类似。这个特点一方面使得该指针简单易用。还有一方面也造成了功能的薄弱——不能用于stl的容器中。
    –不能用于管理数组对象
    因为boost::scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的。假设要管理数组对象须要使用boost::scoped_array类

    shared_ptr

    •缺点OR特性:
    –1. shared_ptr是Boost库所提供的一个智能指针的实现,shared_ptr就是为了解决auto_ptr在对象全部权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了能够共享全部权的智能指针.
    –2. shared_ptr比auto_ptr更安全
    –3. shared_ptr是能够拷贝和赋值的。拷贝行为也是等价的。而且能够被比較,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。

    weak_ptr

    一种弱引用

    3.强引用与弱引用

    一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

    相对而言,弱引用当引用的对象活着的时候不一定存在。不过当它存在的时候的一个引用。弱引用并不改动该对象的引用计数,这意味这弱引用它并不正确对象的内存进行管理,在功能上类似于普通指针,然而一个比較大的差别是。弱引用能检測到所管理的对象是否已经被释放。从而避免訪问非法内存

    当时举的鸭子和鸡的样例这里就不说了


    结果:

    如今的结果就是我们上面看到那些已经封装好的智能指针,至于为什么没有放到结果这里,是由于我想它还会继续发展,会越来越简单方便。




  • 相关阅读:
    人工智能第三课:数据科学中的Python
    人工智能第二课:认知服务和机器人框架探秘
    人工智能第一课:使用分类算法预测糖尿病
    如何加入Microsoft Teams 技术社区
    Python在Office 365 开发中的应用
    《Office 365开发入门指南》上市说明和读者服务
    基于Microsoft Graph打造自己的Timeline应用
    Office 365 应用开发的 .NET Core 模板库
    拥抱开源,Office 365开发迎来新时代
    Excel as a Service —— Excel 开发居然可以这么玩
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6747374.html
Copyright © 2011-2022 走看看