指针是C++从C继承过来的一个强大而有效的工具,指针被人诟病的地方有很多,其中内存管理的艰难性就是其中之一。比如一个在堆中维护的对象,有好几个指针都指向它,那么究竟由谁来负责释放内存呢?小程序还好说,大程序往往很难理清其中的逻辑,一个处理不好就会造成内存泄露这样的严重问题。
智能指针就是这样一种实现机制,它通过一种引用计数器的实现原理,来保证由最后一个指向该对象的指针负责释放内存,将管理内存的工作交由指针自己去完成,减轻了程序的员的负担。下面我们来手动完成一个智能指针的实现:
SmartPointer.h文件
/*
*author: justaipanda
*create time:2012/07/23 15:05:00
*/
#ifndef _SMARTPOINTER_H
#define _SMARTPOINTER_H
#include <stdexcept>
using namespace std;
/*
*description:
* this template class is used as a smart pointer handler
* which can manage the allocated memory
*/
template<class T>
class SmartPointer {
public:
SmartPointer(T* p_ptr = NULL):ptr(p_ptr),
count(new size_t(1)) {
}
SmartPointer(const SmartPointer& sp):ptr(sp.ptr),
count(new size_t(sp.count)) {
++*count;
}
SmartPointer& operator=(const SmartPointer& sp);
bool operator==(const SmartPointer& sp) const {
return ptr == sp.ptr;
}
T& operator*() const {
if (ptr)
return *ptr;
else
throw logic_error("unbunded pointer");
}
T* operator->() const {
if (ptr)
return ptr;
else
throw logic_error("unbunded pointer");
}
~SmartPointer() {
destroy();
}
private:
T* ptr;
size_t* count;//used to recorde the reference times
void destroy();
};
#include "SmartPointer.cpp"
#endif
SmartPointer.cpp文件
/*
*author: justaipanda
*create time:2012/07/23 15:30:15
*/
template<class T>
SmartPointer<T>& SmartPointer<T>::operator=(
const SmartPointer& sp) {
if (ptr != sp.ptr) {
destroy();
ptr = sp.ptr;
count = sp.count;
++*count;
}
return *this;
}
template<class T>
void SmartPointer<T>::destroy() {
if (--*count == 0) {
delete ptr;
delete count;
}
}
由于我们定义的实际上是一个类模板,所以,头文件必须要包含实现文件。
现在我们来看一下程序实现的细节。
在SmartPointer的内部维护了两个指针,一个用于指向所管理的堆中的对象,另一个则是引用计数器。在构造函数中,count被初始化为1,而每当发生复制时,例如拷贝构造函数被调用,或是=运算符被使用,都会导致该计数器加1。而每当智能指针被赋以新值,或该指针本身被销毁时,它都会将引用数减1,然后判断是否为0,如是,则销毁对象。这一部分代码被重构出来是因为赋值重载函数和析构函数都会使用到这一逻辑,在类的内部有一个私有函数destroy专门负责这一部分功能。
通过引用计数器保证了指针的安全性。其他的代码就是保证我们智能指针的可用性了,它将使我们使用智能指针和普通指针一样的方便。指针最主要的功能就是解引用以及执行运算符了,所以重载了这两个运算符就能保证这一点。解引用的逻辑比较简单,我们主要分析一下对指向运算符的重载:
T* operator->() const {
if (ptr)
return ptr;
else
throw logic_error("unbunded pointer");
}
可以看见,它实际上也没做额外的工作,就是在保证指针绑定到一个特定的对象的情况下返回它所维护的指针而已,这里面包含着一个重要的编译特性。先看一个使用智能指针的例子:
/*
*author: justaipanda
*create time:2012/07/23 15:38:19
*/
#include <iostream>
#include "SmartPointer.h"
using namespace std;
class A {
public:
void test() {
cout<< "this is a test function"<< endl;
}
};
int main() {
SmartPointer<A> ptr(new A());
ptr->test();
return 0;
}
注意看调用语句ptr->test();实际上,编译器在编译这条语句时做了特殊的处理,编译器在遇到指向运算符时如果发现调用者是一个重载了指向运算符的类,而并非一个普通的指针,它将做如下替换:
(ptr->)->test();
这样就保证了我们可以正常使用我们的智能指针,而且这种机制是递归的,也就是说,如果指向运算符返回的仍是一个重载了指向运算符的类,而非普通的指针,那么它将做递归的替换。好了,重点的就说这么多吧。