上一篇我们已经详细讲解了智能指针的基础性知识和auto_ptr的模拟实现。
今天呢我们来讲解boost库的发展。
在C++11标准出来之前,C++98标准中都一直只有一个智能指针auto_ptr,我们知道,这是一个失败的设计。它的本质是管理权的转移,这有许多问题。而这时就有一群人开始扩展C++标准库的关于智能指针的部分,他们组成了boost社区,他们负责boost库的开发和维护。其目的是为C++程序员提供免费、同行审查的、可移植的程序库。boost库可以和C++标准库完美的共同工作,并且为其提供扩展功能。现在的C++11标准库的智能指针很大程度上“借鉴”了boost库。
1,scoped_ptr的模拟实现
scoped_ptr是一种简单粗暴的设计,它本质就是防拷贝,避免出现管理权的转移。这是它的最大特点,所以他的拷贝构造和赋值运算符重载只是只声明不定义,但是为了防止有的人在类外定义,所以将函数声明为protected。但这则也是他最大的问题所在,就是不能赋值拷贝,也就是说功能不全。
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr = NULL)
:_ptr(ptr)
{
cout << "ScopedPtr" << endl;
}
~ScopedPtr()
{
delete _ptr;
cout << "~ScopedPtr" << endl;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
bool operator==(const ScopedPtr<T>& s)
{
return _ptr == s._ptr;
}
bool operator!=(const ScopedPtr<T>& s)
{
return _ptr != s._ptr;
}
void Reset(T* ptr = NULL)
{
if (_ptr != ptr)
{
delete _ptr;
}
_ptr = ptr;
}
protected:
ScopedPtr(ScopedPtr<T>& s); //防拷贝(只声明不定义,为防止别人在类外定义,就将他声明为protected)
ScopedPtr<T>& operator=(ScopedPtr<T>& s);
protected:
T* _ptr;
};
2,shared_ptr的模拟实现
这是比较完善的一个智能指针,他是通过指针保持某个对象的共享拥有权的智能指针。若干个shared_ptr对象可以拥有同一个对象,该对象通过维护一个引用计数,记录有多少个shared_ptr指针指向该对象,最后一个指向该对象的shared_ptr被销毁或重置时,即引用计数变为0时,该对象被销毁。销毁对象时使用的是delete表达式或是在构造shared_ptr时传入的自定义删除器(delete),这后面会有详细讲解,但是shared_ptr指针同样拥有缺陷,那就是循环引用,和线程安全问题,这也在后面讲解。先来模拟实现一下shared_ptr指针。
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = NULL)
:_ptr(ptr),_count(new int(0))
{
if (_ptr != NULL)
{
(*_count)++;
}
}
SharedPtr(const SharedPtr<T>& s)
{
_ptr = s._ptr;
_count = s._count;
if (_ptr != NULL)
{
(*_count)++;
}
}
SharedPtr<T>& operator=(const SharedPtr<T>& s)
{
if (this != &s) //排除自己给自己赋值
{
if (--(*_count) <= 0) //正常情况赋值
{
delete _ptr;
delete _count;
}
else //指向同一个对象的指针互相赋值
{}
_ptr = s._ptr;
_count = s._count;
(*_count)++;
}
return *this;
}
~SharedPtr()
{
if (--(*_count) == 0)
{
delete _ptr;
delete _count; //别忘了delete维护的引用计数
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
bool operator ==(const SharedPtr<T>& S)
{
return (_ptr == s._ptr);
}
bool operator !=(const SharedPtr<T>& S)
{
return (_ptr != s._ptr);
}
protected:
T* _ptr;
int* _count;
};
线程安全问题
因为使用引用计数值位判定指标,所以在多线程的环境下是不安全的。会因线程调用的先后顺序不同导致错误产生。对于这种问题,解决方法一般是加锁,对引用计数进行加锁,保证操作是互斥的。(这里暂且说这些,后续文章会有提及。)
循环引用问题
针对循环引用,我来举个例子使大家能更好的理解。看下面代码:
struct ListNode
{
int _data;
shared_ptr<ListNode> _next;
shared_ptr<ListNode> _prev;
ListNode(int x)
:_data(x),_next(NULL),_prev(NULL)
{}
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void test()
{
shared_ptr<ListNode> A(new ListNode(1));
shared_ptr<ListNode> B(new ListNode(2));
//if (A && B) //将这五行代码放开来会出现什么情况
//{
// A->_next = B;
// B->_prev = A;
//}
cout << "A._count:" << A.use_count() << endl;
cout << "B._count:" << B.use_count() << endl;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果
但是如果我将上面代码中屏蔽的那五行放开来,会出现什么结果呢?看下图:
有什么不同?对比上图,可以发现下面图的两个节点维护的引用计数值为2,他们也没有调用析构函数造成内存泄露。这是什么原因造成的?我们用一张图来解释。
而要解决循环引用的问题,就牵扯到了我们后面将要讲的一个指针weak_ptr。具体看后面。
定制删除器(仿函数)
经上面分析,我们可以看到,上面的指针不能用于文件的关闭,也不能用于管理malloc和new[]开辟的动态内存的释放,所以我们可以运用仿函数来定制删除器。如下:
template<class T>
struct DeleteArray //用于new[]开辟的动态内存释放
{
void operator()(T* ptr)
{
cout << "A" << endl;
delete[] ptr;
}
};
struct Fclose //用于文件关闭
{
void operator()(FILE* ptr)
{
cout << "B" << endl;
fclose(ptr);
}
};
template<class T>
struct Free //用于malloc开辟的动态内存的释放
{
void operator()(T* ptr)
{
cout << "C" << endl;
free(ptr);
}
};
int main()
{
shared_ptr<string> ap1(new string[10], DeleteArray<string>());
shared_ptr<FILE> ap2(fopen("test.txt", "w"),Fclose());
shared_ptr<int> ap3((int*)malloc(sizeof(int)), Free<int>());
return 0;
}
3,weak_ptr
weak_ptr是一个辅助性的智能指针,结合shared_ptr指针使用,它的本质就是弱引用,并不增加引用计数值。他没有实现->和*运算符的重载,所以不能直接用它访问对象。针对循环引用这个问题,就是因为不会引起引用计数值的改变,所以我们可以将_next和_prev定义为weak_ptr指针,这样就很好地解决了循环引用的问题。
struct ListNode
{
int _data;
weak_ptr<ListNode> _next; //定义为weak_ptr指针
weak_ptr<ListNode> _prev;
ListNode(int x)
:_data(x),_next(NULL),_prev(NULL)
{}
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
boost库剩余的两个指针:auto_arr和shared_arr.这两个都是管理数组的,因为之前几个指针的析构函数中都是delete,不能对数组进行释放,所以我们自己定制删除器,而这两个指针就是专门管理指针的。下面来模拟实现一下。
模拟实现auto_arr
template<class T>
class AutoArr
{
public:
AutoArr(T* ptr = NULL)
:_ptr(ptr)
{}
~AutoArr()
{
delete[] _ptr;
}
AutoArr(const AutoArr<T>& s)
{
_ptr = s._ptr;
s._ptr = NULL;
}
AutoArr<T>& operator=(const AutoArr<T>& s)
{
if (this != &s)
{
_ptr = s._ptr;
s._ptr = NULL;
}
return *this;
}
T& operator[](size_t pos)
{
if (_ptr == NULL)
{
throw a;
}
return *(_ptr+pos);
}
void set(T* ptr)
{
int i = 0;
while (*(ptr + i))
{
*(_ptr + i) = *(ptr + i);
i++;
}
}
protected:
T* ptr;
};
模拟实现shared_arr
template<class T>
class SharedArr
{
public:
SharedArr(T* ptr = NULL)
:_ptr(ptr),_count(new int(0))
{
(*_count)++;
}
~SharedArr()
{
delete[] _ptr;
}
SharedArr(const SharedArr<T>& s)
{
_ptr = s._ptr;
(*_count)++;
}
SharedArr<T>& operator=(const SharedArr<T>& s)
{
if (this != &s)
{
if (--(*_count) <= 0)
{
delete _ptr;
delete _count;
}
else
{ }
_ptr = s._ptr;
_count = s._count;
(*_count)++;
}
}
T& operator[](size_t pos)
{
if (_ptr == NULL)
{
throw 1;
}
return *(_ptr + pos);
}
protected:
T* _ptr;
int* _count;
};