C++易混知识点4: 自己编写一个智能指针(Reference Counting)学习auto_ptr和reference counting

这篇文章建大的介绍了如何编写一个智能指针。

介绍: 
什么是智能指针?答案想必大家都知道,智能指针的目的就是更好的管理好内存和动态分配的资源,智能指针是一个智能的指针,顾名思义,他可以帮助我们管理内存。不必担心内存泄露的问题。实际上,智能指针是一个行为类似于指针的类,通过这个类我们来管理动态内存的分配和销毁。方便客户端的使用。相比于一般指针,智能指针主要体现在它使用的容易和便捷性。

转载请注明出处: http://blog.csdn.net/elfprincexu
使用一般指针的问题:
一般情况下我们使用指针的问题是什么?答案是内存管理,简单来看下面的一个例子:
char* pName  = new char[1024];
SetName(pName);
if(null != pName)
{
       delete[] pName; 
}
在上面一段代码中,我们会遇到bug呢? 很有可能在分配内存的时候就出错了,有可能在被调用的时候指针误操作了,也有可能在其他地方操作了。答案太多太多了
我们还是从一个实际的例子开始吧,首先看下面的例子:
class Person
{
    int age;
    char* pName;
    public:
        Person(): pName(0),age(0){}
        Person(char* pName, int age): pName(pName), age(age){}
        ~Person(){}

        void Display()
        {
            printf("Name = %s Age = %d \n", pName, age);
        }
        void Shout()
        {
            printf("Ooooooooooooooooo",);
        } 
};
现在,我们开始使用这个类
void main()
{
    Person* pPerson  = new Person("Scott", 25);
    pPerson->Display();
    delete pPerson;
}
我们可以看到,每次我们新建一个person空间,都要对内存释放,否则就有可能造成内存泄露。
那我们能不能想象一下,存在一个类似指针的类来帮助我们管理内存。
template < typename T > class SP
{
    private:
    T*    pData; // Generic pointer to be stored
    public:
    SP(T* pValue) : pData(pValue){}
    ~SP()
    {
        delete pData;
    }

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

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

void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    // Dont need to delete Person pointer..
}
通过泛型编程,我们可以使用任何类型的指针,但是上面还不是完美,考虑一下下面的案例
void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    {
        SP<PERSON> q = p;
        q->Display();
        // Destructor of Q will be called here..
    }
    p->Display();
}
我们发现,p和q指向同一个实例,由于SP类没有定义拷贝函数,系统自动生成一个默认的拷贝函数,实现的是浅赋值,该内存空间被释放了两次!
所以,我们引入Reference Counting的智能指针至关重要,通过对实例被引用的次数来决定该实例是否需要被释放。
class RC
{
    private:
    int count; // Reference count

    public:
    void AddRef()
    {
        // Increment the reference count
        count++;
    }

    int Release()
    {
        // Decrement the reference count and
        // return the reference count.
        return --count;
    }
};
现在我们有了一个RC类,这个类只做被引用的次数,唯一的数据成员就是用来跟踪被引用的次数。
结合我们刚才的SP类,我们稍作改动
template < typename T > class SP
{
private:
    T*    pData;       // pointer
    RC* reference; 	   // Reference count

public:
    SP() : pData(0), reference(0) 
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(T* pValue) : pData(pValue), reference(0)
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
    {
        // Copy constructor
        // Copy the data and reference pointer
        // and increment the reference count
        reference->AddRef();
    }

    ~SP()
    {
        // Destructor
        // Decrement the reference count
        // if reference become zero delete the data
        if(reference->Release() == 0)
        {
            delete pData;
            delete reference;
        }
    }

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

    T* operator-> ()
    {
        return pData;
    }
    
    SP<T>& operator = (const SP<T>& sp)
    {
        // Assignment operator
        if (this != &sp) // Avoid self assignment
        {
            // Decrement the old reference count
            // if reference become zero delete the old data
            if(reference->Release() == 0)
            {
                delete pData;
                delete reference;
            }

            // Copy the data and reference pointer
            // and increment the reference count
            pData = sp.pData;
            reference = sp.reference;
            reference->AddRef();
        }
        return *this;
    }
};
接下来我们看下客户端调用情况
void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    {
        SP<PERSON> q = p;
        q->Display();
        // Destructor of q will be called here..

        SP<PERSON> r;
        r = p;
        r->Display();
        // Destructor of r will be called here..
    }
    p->Display();
    // Destructor of p will be called here 
    // and person pointer will be deleted
}

接下来,我们着重分析下上面的调用情况:
1. SP<PERSON> p(new Person("Scott",25));
当我们创建一个新的智能指针的时候,他的参数类型为Person, 参数为一个Person的普通指针, 智能指针p中的情况是
构造函数被调用,pData 复制新创建的person指针, 同时新建一个RC成员,同时RC调用addReference()函数, reference.count =1 ;
2. SP<PERSON> q = p;
接下来,我们有定义了一个新的SP智能指针q, 调用SP类的拷贝构造函数,q的pData同样复制p的pData的值,q的reference拷贝p的reference值
同时,我们发现,q的reference.count加1, 现在 q的reference.count =2;
3. SP<PERSON> r; r = p;
接下来,我们创建一个新的空的智能指针r,并调用assigne operator 赋值函数初始化,同样,由于r != p, 所以原来的r的空间会被释放, 然后将p的空间复制给r。
这个时候r的pData同样指向Person实例的地址,p的reference复制p的reference,并且对reference加1.   现在 r的reference.count =3.

4. 由于 r,q 生命域到达,rq 的析构函数先后被调用。
r首先被析构, 会对reference.count减一,等于2,发现还没到0, 所以不会释放 pdata 和 reference
q其次被析构,会对reference.count减一,等于1,发现还没到0, 所以不会释放 pdata 和 reference
5. p的生命域到达,p调用析构函数
p最后被析构,会对reference.count减一,等于0,发现到0, 所以释放 pdata 和 reference。 此时pdata 就是一开始新创建的Person空间,所以person会被释放,同时Reference也会被释放。

总结:
整个过程中,我们只创建了一次Person实例和Reference实例, 但最多有三个智能指针同时指向他们,通过对实例的被引用次数记录,来“智能”的判断什么时候释放真正的内存空间。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值