Chinaunix首页 | 论坛 | 博客
  • 博客访问: 389102
  • 博文数量: 80
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 560
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-10 08:38
文章分类
文章存档

2016年(32)

2015年(48)

我的朋友

分类: C/C++

2016-04-18 14:05:12

智能指针实际上是一个类(class),里面封装了一个指针.它的用处是啥呢?

指针与内存

    说到指针自然涉及到内存.我们如果是在堆栈(stack)中分配了内存, 用完后由系统去负责释放. 如果是自定义类型,就会自动的去调用你的析构函数.

    但如果是在堆(heap)中分配了内存,也就是用malloc或者new.那只能自动手动的使用free或delete去释放. 所以使用heap时处理的不好很容易出现啥内存泄露(内存没有释放掉). 或者如果你delete一次了, 但没让它赋值为0,然后再delete一次就导致未定义行为了.

    于是你想如果系统能也像管理stack一样来管理你的heap区域.不用再担心内存的分配与释放该多好啊, 事实上Java,C#都这样去做了, 也给你去管理heap区域了(所有的自定义类型实例化时都去heap区域获取内存了, 也没有提供指针的功能,  你想自己去释放内存都不给你这机会了, JVM或者CLR会在后台自动的去给你释放掉那些不用的内存,当然这样一来效率自然没有你手动释放来的高了.)

    假如有一个指针指向一块分配的内存, 智能指针就是把该指针封装起来, 然后用完了会自动去释放内存(通过智能指针类的析构函数). 这样你就不用担心没有去释放内存了. 当然并不是说你使用了智能指针就能像使用Java, C#一样不用再担心内存问题了. 智能指针在使用的时候还会存在很多的问题.

    据说JVM,CRL也是用(c与c++)实现的.不知道那里面是否也有用到智能指针......

 

智能指针的实现

如果你自己要封装一个指针你会咋整呢?来来个初稿瞧瞧

1.最精简版本

template< class T>

class my_auto_ptr {

public:

    T* m_ptr;  //被封装的指针

public:

    my_auto_ptr( T* p) :m_ptr( p ) { }   //构造函数

    ~my_auto_ptr() { delete m_ptr; }     //析构函数

};

上面的自然是最精简版的,只一个成员变量,构造函数和析造函数. 不过虽然简单其实也能拿来用了啊. 比如:

my_auto_ptr myPtr( new int(88) ); //等价int* ip = new int(88); 但这样你得手动delete ip; 而用了智能指针就不用手动delete了.

cout<< *myPtr.m_ptr;   //相当于cout<<*ip;

 

2.改进版本(重载运算符使类用起来像指针)

上面的的精简版本用起来还挺麻烦.我们是希望封装了指针类用起来跟指针本身一样才好.所以需要重载-> , * 等运算符

template< class T>

class my_auto_ptr {

private:
    
T* m_ptr; //被封装的指针

public:

    my_auto_ptr( T* p) :m_ptr( p ) { }

    ~my_auto_ptr() { delete m_ptr; }

    T& operator*() { return *m_ptr;}

    T*  operator->() { return m_ptr;}

};

现在my_auto_ptr可以变得很像指针了

my_auto_ptr mp(new int(88) );   //等价int* ip = new int(88);

int num = *mp;   //等价int num = *ip;

假如有这样的类struct Arwen { void Test() { cout<"i am arwen"<<; }

则my_auto_ptr mp( new Arwen);   //等价Arwen* ip = new Arwen;

mp->Test();  //等价ip-Test();

 

 

3.完善版本(复制构造)

一个完善点的类往往还涉及到复制构造的一些操作.也可以做把另外一个智能指针类做为构造函数的参数,或者通过=给一个类赋值

template< class T>

class my_auto_ptr {

private:

    T* m_ptr;

    T* GetPtr(){ //供构造赋值时使用

    T* tmp = m_ptr;

    m_ptr = 0;

    return tmp;

}

public:

    explicit my_auto_ptr( T* p = 0) :m_ptr( p ) { }

    ~my_auto_ptr() { delete m_ptr; }

    T& operator*() { return *m_ptr;}

    T* operator->() { return m_ptr;}

 

    my_auto_ptr(my_auto_ptr& mp)
    {   //复制构造函数

        m_ptr = mp.GetPtr(); //mp复制过来后它自己原来的指针相当于失效了.

    }

    my_auto_ptr& operator=(my_auto_ptr& ap)
    { //赋值操作符

        if(ap != *this)

        {

            delete m_ptr;

            m_ptr = ap.GetPtr();

        }

        return *this;

    }

 

    void reset(T* p)
    {  //指针重置,相当于把指针指向另外一个地方去

        if(p != m_ptr)

            delete m_ptr;

        m_ptr = p;

    }

};

 使用举例:

如有类:
struct Arwen{

    int age;

    Arwen(int gg) :age(gg) { };

};

 

void main()

{

    my_auto_ptr myPtr( new Arwen(24) );

    int num =myPtr->age; //正确

 

     my_auto_ptr ptrOne( myPtr);  //复制构造

     //num =myPtr->age; 该处会出错.因为把myPtr复制给ptrOne后,它自己本身相当于失效了

      num = ptrOne->age; //正确

 

     my_auto_ptr ptrTwo = ptrOne;

     //num = ptrOne->age;该处也会出错,此时ptrOne也失效了

     num = ptrTwo->age; //正确

 

    Arwen* pArwen = new Arwen( 88 );

    ptrTwo.reset( pArwen);

    num = pArwen->age; //此处的值是88了,而不是以前的24

 

    return 0;
}

 auto_ptr的缺陷

上面我实现的my_auto_ptr基本上是实现了auto_ptr的所有核心功能.从里面我们可以明显的看到一个很大缺陷.我们看到当通过复构造函数,通过操作符=赋值后,原来的那个智能指针对象就失效了.只有新的智能指针对象可以有效使用了.用个专业点的说法叫所有权的转移.被包装的指针指向的内存块就像是一份独享占用的财产,当通过复制构造,通过=赋值传给别的智能指针对象时,原有的对象就失去了对那块内存区域的拥有权.也就是说任何时候只能有一个智能指针对象指向那块内存区域,不能有两个对象同时指向那块内存区域.

这样一来auto_ptr不能做为STL中容器的元素,为啥呢? 因为容器中经常存在值拷贝的情况嘛,把一个容器对象直接赋值给另一对象.完了之后两个容器对象可得都能用啊.而如果是auto_ptr的话显然赋值后只能一个有用,另一个完全报废了.另外比如你有个变量auto_ptr ap( new int(44) );  然后ap被放进一个容器后,ap就报废不能用了.

不过没办法啊,在c++ 11标准之前,现在我们大部分用的是98标准吧,STL里面只有auto_ptr这一种智能指针.而在11标准中除了auto_ptr还有如下三种:

 

unique_ptr

smart pointer with unique object ownership semantics

只能有一个主人的指针,可以用于STL容器

shared_ptr

smart pointer with shared object ownership semantics

可共享的指针

weak_ptr

weak reference to an object managed by std::shared_ptr

弱引用指针

阅读(1996) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~