c/c++中内存的动态分配的结果是一个指针,该指针指向该片堆内存空间。c/c++中并没有垃圾回收机制,而用户使用完该堆空间后又可能忘记释放该片堆内存空间,这就造成内存泄漏。一个行之有效的解决办法就是采用智能指针代替原始指针,弥补原生指针无法控制所指堆空间的生命期的空缺。
在c++11标准中有多个智能指针,其中包括共享指针shared_ptr和独占指针unique_ptr。本文将实现SharedPointer和UniquePointer来效仿这两个智能指针。
类的继承结构UML图如下:
Pointer是一个基类,封装了SharedPointer和UniquePointer共有的操作函数:
//Pointer.h
#ifndef __POINTER_H__
#define __POINTER_H__
template<typename T>
class Pointer
{
protected:
T* m_pointer; //指向用户动态分配的内存空间
public:
//构造函数,为m_pointer赋值
Pointer(T* p = NULL) { m_pointer = p; }
//指针关联的操作符的重载函数
T* operator->() { return m_pointer; }
T& operator*() { return *m_pointer; }
//为const对象使用
T* operator->() const { return m_pointer; }
T& operator*() const { return *m_pointer; }
bool isNull() const { return m_pointer == NULL; }
T* get() const { return m_pointer; }
virtual ~Pointer() {}
};
#endif /* __POINTER_H__ */
UniquePointer指针称为独占指针,其设计要点为:
(1)指针生命周期结束(所在函数退出)时主动释放堆空间;
(2)一片堆空间至多只能由一个指针标识;
#ifndef __UNIQUEPOINTER_H__
#define __UNIQUEPOINTER_H__
#include "Pointer.h"
template<typename T>
class UniquePointer : public Pointer<T>
{
public:
UniquePointer(T* p = NULL) : Pointer<T>(p) //构造函数调用父类的构造函数
{}
UniquePointer(const UniquePointer<T>& obj) //拷贝构造函数
{
//确保obj.m_pointer指向的堆空间只能由一个UniquePointer指针指向
this->m_pointer = obj.m_pointer;
const_cast<UniquePointer<T>& >(obj).m_pointer = NULL;
}
UniquePointer<T>& operator=(const UniquePointer<T>& obj)
{
if (this != &obj)
{
T* p = this->m_pointer;
//确保obj.m_pointer指向的堆空间只能由一个UniquePointer指针指向
this->m_pointer = obj.m_pointer;
const_cast<UniquePointer<T>& >(obj).m_pointer = NULL;
//最后在delete,确保异常安全
delete p;
}
return *this;
}
~UniquePointer()
{
//释放堆空间
delete this->m_pointer;
}
};
#endif /* __UNIQUEPOINTER_H__ */
使用UniquePointer指针时需要注意:
(1)只能使用UniquePointer来指向堆空间中单个对象或者变量的内存空间,不能指向数组空间(析构函数是delete而非delete[])
(2)交付UniquePointer的指针不能手动delete(否则会因在UniquePointer析构函数再delete而运行时崩溃)
//main.cpp
#include <iostream>
#include "UniquePointer.h"
#include "SharedPointer.h"
using namespace std;
class Ctest
{
public:
int a;
Ctest(int m) : a(m) { cout << "Ctest()" << endl; }
~Ctest() { cout << "~Ctest()" << endl; }
};
int main(void)
{
UniquePointer<Ctest> upc1 = new Ctest(6);
cout << "upc1.get() = " << upc1.get() << endl;
UniquePointer<Ctest> upc2 = upc1;
cout << "upc1.get() = " << upc1.get() << endl;
cout << "upc2.get() = " << upc2.get() << endl;
return 0;
}
makefile:
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, %.o, $(SRC))
all : clean a.out
a.out : main.o
g++ $< -o $@ -Wall
%.o : %.cpp
g++ -c $< -o $@ -Wall
.PHONY : clean
clean:
rm -rf $(OBJ) a.out
编译运行:
SharedPointer指针称为共享指针,其实现要点为:
(1)通过计数机制来标识指向堆内存的指针的个数:指向一块堆内存时,该堆空间的计数值计数加1;指针被置空或者指向其它堆内存时,原堆空间的计数值减1;计数值为0时(无指针指向该堆内存)释放堆内存。
(2)因为一片堆空间可由多个SharedPointer指针指向,所以允许SharedPointer类之间的==和!=操作,以判断两个SharedPointer对象是否指向同一片堆空间。
//SharedPointer.h
#ifndef __SHAREDPOINTER_H__
#define __SHAREDPOINTER_H__
#include <stdlib.h>
#include <stdexcept>
#include "Pointer.h"
template<typename T>
class SharedPointer : public Pointer<T>
{
protected:
int *m_ref;
void _clear() //递减计数值,置空m_pointer和m_ref。若计数值为0则销毁指向的堆空间
{
T* toDel = this->m_pointer;
int* ref = this->m_ref;
this->m_pointer = NULL;
this->m_ref = NULL;
if (ref)
{
--(*ref);
if (*ref == 0)
{
free(ref);
//为确保异常全,最后才delete
delete toDel;
}
}
}
public:
//确保异常安全,在this->m_ref动态分配成功后再为基类的m_pointer赋值
SharedPointer(T* p = NULL) : /*Pointer<T>(p), */m_ref(NULL)
{
if (p)
{
this->m_ref = static_cast<int* >(malloc(sizeof(int)));
if (this->m_ref)
{
*(this->m_ref) = 1;
this->m_pointer = p;
}
else
{
throw(std::out_of_range("SharedPointer(T* p = NULL): malloc memory failed"));
}
}
}
SharedPointer(const SharedPointer<T>& obj)
{
//指针指向obj所维护的堆空间,并自加该堆空间的引用计数
this->m_ref = obj.m_ref;
this->m_pointer = obj.m_pointer;
if (this->m_ref)
++(*(this->m_ref));
}
SharedPointer<T>& operator=(const SharedPointer<T>& obj)
{
if (this != &obj)
{
//先清除自身维护的堆空间
_clear();
//再指向obj所维护的堆空间,并自加该堆空间的引用计数
this->m_ref = obj.m_ref;
this->m_pointer = obj.m_pointer;
if (this->m_ref)
++(*(this->m_ref));
}
return *this;
}
~SharedPointer()
{
_clear();
}
};
//SharedPointer的比较函数。一般比较函数会设计为全局函数
template <typename T>
bool operator==(const SharedPointer<T>& left, const SharedPointer<T>& right)
{
return (left.get() == right.get());
}
template <typename T>
bool operator!=(const SharedPointer<T>& left, const SharedPointer<T>& right)
{
//return !(left.get() == right.get());
return !(left == right);
}
#endif /* __SHAREDPOINTER_H__ */
使用SharedPointer指针:
int main(void)
{
const SharedPointer<Ctest> spc1 = new Ctest(6);
const SharedPointer<Ctest> spc2 = spc1;
SharedPointer<Ctest> spc3;
cout << spc2->a << endl;
cout << "spc1.get() = " << spc1.get() << endl;
cout << "spc2.get() = " << spc2.get() << endl;
cout << "spc3.get() = " << spc3.get() << endl;
if (spc2 != spc1 )
cout << "spc3 != spc1" << endl;
else
cout << "spc3 == spc1" << endl;
return 0;
}
编译运行:
SharedPointer的使用注意项和UniquePointer的类似,另外还需要注意不同类型的智能指针不可以混合使用。