一种基于引用计数的智能指针的实现
2014-06-10 18:04
375 查看
介绍
什么是智能指针?答案很简单。智能指针就是一个智能的指针。什么意思呢?就是说,智能指针是这样一类对象:他们的行为像指针,但是他们干的活又比一般的指针多。智能指针既像普通的指针一样灵活,又能充分利用对象的优势(例如智能指针可以和对象一样,构造函数和析构函数自动被调用)。智能指针是用来处理普通指针引发的问题的(这也就是为什么称之为智能)
普通指针的缺陷
例如你有下面一段代码
char* pName = new char[1024];
…
SetName(pName);
…
…
if(null != pName)
{
delete[] pName;
}
我们太多次的因为忘记删除pName,而引发bug。如果有人能够为我们释放不再有用的内存该多好呀(当然我们不考虑垃圾回收的机制)。如果指针自己能够处理释放的问题就好了。这就是智能指针要干的活。让我们写一个智能指针,来看看可以怎样更好的处理指针。
我们举一个实际一点的例子。假设我们有一个Person 类。
class Person
{
intage;
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",);
}
};
下面是Person类客户端的代码 (客户端就是使用Person类的代码)
void main()
{
Person* pPerson = newPerson("Scott", 25);
pPerson->Display();
delete pPerson;
}
上面的代码,我们每new一个对象就要delete一次。我们想避免这样的麻烦。我们需要一些自动化的机制,析构函数闯进我的脑海。但是普通的指针没有析构函数。所以我们的智能指针需要有一个析构函数。我们将创建一个SP 类,SP 类将握着Person 类的指针,SP 类的析构函数被调用的时候要释放Person 类的内存。代码就可以变成下面的样子。
void main()
{
SPp(new Person("Scott", 25));
p->Display();
//Dont need to delete Person pointer..
}
需要注意以下几点。
我们创建了SP 类的对象,这个对象握着Person 类的指针。当SP 对象所在的作用域结束的时候SP 对象将被析构,该对象负责释放Person 的指针,这样我们就不必自己手动释放了。
另外很重要的一点就是我们需要像使用Person 类的指针那样使用SP 类的对象。p->Display();p就好像是一个Person的指针。
智能指针的接口
既然智能指针应该表现的像普通指针一样,它就应该和普通指针有同样的对外接口。比如他需要支持一下两个操作符:
Dereferencing (operator *)
Indirection (operator ->)
我们来实现SP 类
class SP
{
private:
Person* pData; // pointer toperson class
public:
SP(Person* pValue) : pData(pValue)
{
}
~SP()
{
// pointer no longer requried
delete pData;
}
Person& operator* ()
{
return *pData;
}
Person* operator-> ()
{
return pData;
}
};
让我们的智能指针类更通用一些
使用模板修改我们的代码:
template < typename T > class SP
{
private:
T* pData; // Generic pointer tobe 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 指向同一个Person对象。当q的作用域结束,q执行析构函数,会释放Person对象。之后我们就不能调用 p->Display();了。应为p现在指向的是一个悬空的指针,就是一个野指针,调用将失败。直到没有任何人指向Person对象的时候我们才应该释放Person
对象。该怎么办呢?实现一个引用计数机制可以帮我们解决这个问题。
引用计数
我们写一个新的类RC,这个类将维护一个代表引用次数的整数。
class RC
{
private:
intcount; // Reference count
public:
void AddRef()
{
// Increment the reference count
count++;
}
intRelease()
{
// Decrement the reference count and
// return the reference count.
return --count;
}
};
现在我们有了一个引用计数类,我们将把RC和SP联系起来。我们的SP类中将维护一个RC对象的指针。不同的SP对象只要指向的是同一个普通对象,这些SP对象就以共享一个RC对象。为此,我们需要重写SP类的 赋值构造函数 和 拷贝构造函数。
template < typename T > class SP
{
private:
T* pData; // 普通指针
RC*reference; // 引用计数
public:
SP() : pData(0), reference(0) // 空 构造函数
{
// new 一个引用计数类
reference = new RC();
// 引用计数 + 1
reference->AddRef();
}
SP(T* pValue) : pData(pValue), reference(0) // 带参数的构造函数
{
// new 一个引用计数类
reference = new RC();
// 引用计数 + 1
reference->AddRef();
}
SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
{
// 拷贝构造函数,拷贝 普通指针 和 引用计数,注意都只是拷贝他们的指针。
reference->AddRef();
}
~SP()
{
// 析构
if(reference->Release() == 0)
{
delete pData;
delete reference;
}
}
T& operator* ()
{
return *pData;
}
T*operator-> ()
{
return pData;
}
SP<T>& operator = (const SP<T>& sp)
{
// 赋值构造函数
if (this != &sp) // 自己给自己赋值的时候什么也不干
{
// 减少老的引用计数,如果减到了0 就释放之
if(reference->Release() == 0)
{
delete pData;
delete reference;
}
// 把 普通指针和RC类的指针拷贝过来,引用计数 + 1
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();
SP<PERSON> r;
r = p; // 赋值操作符
r->Display();
// q r 将在此析构
}
p->Display();
//p析构,并将Person类释放掉
}
引用
当使用异常的时候,智能指针就显得特别有用。例如我们有下面一段代码:
void MakeNoise()
{
Person* p = new Person("Scott", 25);
p->Shout();
delete p;
}
看上去挺好,但是Shout 方法里面如果抛异常怎么办? delete p;永远不会被执行,内存泄露了。那我们来处理异常吧:
void MakeNoise()
{
Person* p = new Person("Scott", 25);
try
{
p->Shout();
}
catch(...)
{
delete p;
throw;
}
delete p;
}
捕获异常用重新抛出去,是不是有点费力。如果我们new了很多指针,代码就会变得更加笨重。如果引入智能指针呢?
void MakeNoise()
{
SP<Person> p(new Person("Scott", 25));
p->Shout();
}
我们在这里使用了智能指针,我们不必捕获异常了。如果Shout函数抛出了异常,函数栈将会回退,期间会调用p的析构函数。我们的申请的空间得以成功释放。
什么是智能指针?答案很简单。智能指针就是一个智能的指针。什么意思呢?就是说,智能指针是这样一类对象:他们的行为像指针,但是他们干的活又比一般的指针多。智能指针既像普通的指针一样灵活,又能充分利用对象的优势(例如智能指针可以和对象一样,构造函数和析构函数自动被调用)。智能指针是用来处理普通指针引发的问题的(这也就是为什么称之为智能)
普通指针的缺陷
例如你有下面一段代码
char* pName = new char[1024];
…
SetName(pName);
…
…
if(null != pName)
{
delete[] pName;
}
我们太多次的因为忘记删除pName,而引发bug。如果有人能够为我们释放不再有用的内存该多好呀(当然我们不考虑垃圾回收的机制)。如果指针自己能够处理释放的问题就好了。这就是智能指针要干的活。让我们写一个智能指针,来看看可以怎样更好的处理指针。
我们举一个实际一点的例子。假设我们有一个Person 类。
class Person
{
intage;
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",);
}
};
下面是Person类客户端的代码 (客户端就是使用Person类的代码)
void main()
{
Person* pPerson = newPerson("Scott", 25);
pPerson->Display();
delete pPerson;
}
上面的代码,我们每new一个对象就要delete一次。我们想避免这样的麻烦。我们需要一些自动化的机制,析构函数闯进我的脑海。但是普通的指针没有析构函数。所以我们的智能指针需要有一个析构函数。我们将创建一个SP 类,SP 类将握着Person 类的指针,SP 类的析构函数被调用的时候要释放Person 类的内存。代码就可以变成下面的样子。
void main()
{
SPp(new Person("Scott", 25));
p->Display();
//Dont need to delete Person pointer..
}
需要注意以下几点。
我们创建了SP 类的对象,这个对象握着Person 类的指针。当SP 对象所在的作用域结束的时候SP 对象将被析构,该对象负责释放Person 的指针,这样我们就不必自己手动释放了。
另外很重要的一点就是我们需要像使用Person 类的指针那样使用SP 类的对象。p->Display();p就好像是一个Person的指针。
智能指针的接口
既然智能指针应该表现的像普通指针一样,它就应该和普通指针有同样的对外接口。比如他需要支持一下两个操作符:
Dereferencing (operator *)
Indirection (operator ->)
我们来实现SP 类
class SP
{
private:
Person* pData; // pointer toperson class
public:
SP(Person* pValue) : pData(pValue)
{
}
~SP()
{
// pointer no longer requried
delete pData;
}
Person& operator* ()
{
return *pData;
}
Person* operator-> ()
{
return pData;
}
};
让我们的智能指针类更通用一些
使用模板修改我们的代码:
template < typename T > class SP
{
private:
T* pData; // Generic pointer tobe 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 指向同一个Person对象。当q的作用域结束,q执行析构函数,会释放Person对象。之后我们就不能调用 p->Display();了。应为p现在指向的是一个悬空的指针,就是一个野指针,调用将失败。直到没有任何人指向Person对象的时候我们才应该释放Person
对象。该怎么办呢?实现一个引用计数机制可以帮我们解决这个问题。
引用计数
我们写一个新的类RC,这个类将维护一个代表引用次数的整数。
class RC
{
private:
intcount; // Reference count
public:
void AddRef()
{
// Increment the reference count
count++;
}
intRelease()
{
// Decrement the reference count and
// return the reference count.
return --count;
}
};
现在我们有了一个引用计数类,我们将把RC和SP联系起来。我们的SP类中将维护一个RC对象的指针。不同的SP对象只要指向的是同一个普通对象,这些SP对象就以共享一个RC对象。为此,我们需要重写SP类的 赋值构造函数 和 拷贝构造函数。
template < typename T > class SP
{
private:
T* pData; // 普通指针
RC*reference; // 引用计数
public:
SP() : pData(0), reference(0) // 空 构造函数
{
// new 一个引用计数类
reference = new RC();
// 引用计数 + 1
reference->AddRef();
}
SP(T* pValue) : pData(pValue), reference(0) // 带参数的构造函数
{
// new 一个引用计数类
reference = new RC();
// 引用计数 + 1
reference->AddRef();
}
SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
{
// 拷贝构造函数,拷贝 普通指针 和 引用计数,注意都只是拷贝他们的指针。
reference->AddRef();
}
~SP()
{
// 析构
if(reference->Release() == 0)
{
delete pData;
delete reference;
}
}
T& operator* ()
{
return *pData;
}
T*operator-> ()
{
return pData;
}
SP<T>& operator = (const SP<T>& sp)
{
// 赋值构造函数
if (this != &sp) // 自己给自己赋值的时候什么也不干
{
// 减少老的引用计数,如果减到了0 就释放之
if(reference->Release() == 0)
{
delete pData;
delete reference;
}
// 把 普通指针和RC类的指针拷贝过来,引用计数 + 1
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();
SP<PERSON> r;
r = p; // 赋值操作符
r->Display();
// q r 将在此析构
}
p->Display();
//p析构,并将Person类释放掉
}
引用
当使用异常的时候,智能指针就显得特别有用。例如我们有下面一段代码:
void MakeNoise()
{
Person* p = new Person("Scott", 25);
p->Shout();
delete p;
}
看上去挺好,但是Shout 方法里面如果抛异常怎么办? delete p;永远不会被执行,内存泄露了。那我们来处理异常吧:
void MakeNoise()
{
Person* p = new Person("Scott", 25);
try
{
p->Shout();
}
catch(...)
{
delete p;
throw;
}
delete p;
}
捕获异常用重新抛出去,是不是有点费力。如果我们new了很多指针,代码就会变得更加笨重。如果引入智能指针呢?
void MakeNoise()
{
SP<Person> p(new Person("Scott", 25));
p->Shout();
}
我们在这里使用了智能指针,我们不必捕获异常了。如果Shout函数抛出了异常,函数栈将会回退,期间会调用p的析构函数。我们的申请的空间得以成功释放。
相关文章推荐
- 一种基于引用计数机制的智能指针实现
- 一种基于引用计数机制的智能指针实现
- 基于引用计数的智能指针实现
- c++基于引用计数的智能指针实现
- C++ 引用计数 智能指针简易实现
- 智能指针的一种实现
- c++ 一种智能指针的实现
- 基于auto_ptr源码的智能指针实现
- C++ 智能指针的一个实现(基于模板和Shared_ptr)
- 基于引用计数的智能指针为什么会发生循环引用
- 智能指针的一种实现
- 一种基于UDP协议实现P2P智能穿越NAT的方案
- 基于引用计数的智能指针
- c++ 智能指针的一种实现方法
- (转)智能指针的另外一种实现 - Smart pointers in Delphi
- 一种智能指针的实现方式
- C++一种智能指针的实现
- 网络编程资料总结(四)----一种基于UDP协议实现P2P智能穿越NAT的方案
- (转)智能指针的另外一种实现 - Smart pointers in Delphi
- 智能指针的两种实现(引用计数)