POCO C++库学习和分析 -- 内存管理 (一)
POCO C++库学习和分析 -- 内存管理 (一)
对于内存的管理,Poco C++库中主要包含了引用计数,智能指针,内存池等几个部分。下面将分别对这几个部分进行介绍。首先回顾一下,对于内存的管理,出现过的几种技术。C时代的内存池,主要解决内存碎片,和内存的频繁获取和释放的开销问题。到了C++时代,内存池仍然存在,但是出现了面对对象分配的内存池,解决问题还是一样。C++中智能指针,如STL中的auto_ptr,boost库中share_ptr等。通过把堆上对象的委托给智能指针(智能指针本身可以看成是一个栈对象),并在智能指针内部实现引用计数,当引用计数为0时,删除堆对象,从而达到让编译器自动删除堆对象的目的,实现了堆对象的自动管理。Java和C#的垃圾收集,在语言层次分装,所有的对象都在堆上分配,然后交由语言本身管理,程序员无需关心对象内存的释放。
1. 引用计数和智能指针概述:
对于C和C++来说,堆上内存的管理是交由程序员完成的,程序员如果在堆上分配了一块内存,就必须负责释放掉。如果不小心,就会造成内存泄露。因此所有C/C++程序员设计程序时,对指针和内存的管理都会如履薄冰,非常的小心。而Java和C#则不同,虽然所有对象都被放在堆上,但由于语言本身存在垃圾收集机制,程序员不再需要关心对象的释放。这或多或少的能够使程序员更多的把精力放在其业务编程上。
讲到这里,就顺便扯开去,讲一些题外话。对于某些编程技术讨论时的一些看法。如同制造业一样,制造业存在很多种类,对制造业的划分方法当然也很多。在制造业中存在一个特殊的种类,装备制造业。也就是制造机器的制造业。对于程序员来说也是一样,绝大多数程序员都是面对业务进行编程的,而极少数程序员则是为了制造编程工具或者提供更方便的编程方法而编制程序。这个区别往往导致,不同程序员看问题的角度不同,结果当然也不同。我想很多时候,问题的答案都是不唯一的。接下去继续讨论Poco吧。
通过引用计数和智能指针机制,C++也可以完成了某种意义上的垃圾收集的工作。程序员通过使用智能指针,同样不需要再关注堆对象的释放,当然胶水代码还是需要的。在Poco库中存在两种智能指针,AutoPtr和SharedPtr。
1.1 引用计数(Reference Counting)
1.2 对象所有权(Object Ownership)
1.3 引用计数和对象所有权的关系
2 AutoPtr
2.1 Poco中AutoPtr的例子
#include "Poco/AutoPtr.h"
#include "Poco/RefCountedObject.h"
class A: public Poco::RefCountedObject
{
};
int main(int argc, char** argv)
{
Poco::AutoPtr<A> p1(new A);
A* pA = p1;
// Poco::AutoPtr<A> p2(pA); // BAD! p2 assumes sole ownership
Poco::AutoPtr<A> p2(pA, true); // Okay: p2 shares ownership with p1
Poco::AutoPtr<A> p3;
// p3 = pA; // BAD! p3 assumes sole ownership
p3.assign(pA, true); // Okay: p3 shares ownership with p1
return 0;
}
class Foundation_API RefCountedObject /// A base class for objects that employ /// reference counting based garbage collection. /// /// Reference-counted objects inhibit construction /// by copying and assignment. { public: RefCountedObject(); /// Creates the RefCountedObject. /// The initial reference count is one. void duplicate() const; /// Increments the object's reference count. void release() const; /// Decrements the object's reference count /// and deletes the object if the count /// reaches zero. int referenceCount() const; /// Returns the reference count. protected: virtual ~RefCountedObject(); /// Destroys the RefCountedObject. private: RefCountedObject(const RefCountedObject&); RefCountedObject& operator = (const RefCountedObject&); mutable AtomicCounter _counter; };RefCountedObject原来是一个引用计数对象,其中封装了原子计数类AtomicCounter。实现了两个接口,其中duplicate()用来增加引用计数数目,每次调用引用计数增加1;release()用来减少引用计数数目,每次调用引用计数减少1.
#include "Poco/AutoPtr.h" using Poco::AutoPtr; class RCO { public: RCO(): _rc(1) { } void duplicate() { ++_rc; // Warning: not thread safe! } void release() { if (--_rc == 0) delete this; // Warning: not thread safe! } private: int _rc; }; int main(int argc, char** argv) { RCO* pNew = new RCO; // _rc == 1 AutoPtr<RCO> p1(pNew); // _rc == 1 AutoPtr<RCO> p2(p1); // _rc == 2 AutoPtr<RCO> p3(pNew, true); // _rc == 3 p2 = 0; // _rc == 2 p3 = 0; // _rc == 1 RCO* pRCO = p1; // _rc == 1 p1 = 0; // _rc == 0 -> deleted // pRCO and pNew now invalid! p1 = new RCO; // _rc == 1 return 0; } // _rc == 0 -> deleted
2.2 Poco中AutoPtr的类图
从类图中可以看出,Poco中AutoPtr类是和RefCountedObject是配套使用的,如果用户类继承自RefCountedObject,就可以由AutoPtr实现垃圾收集。
2.3 Poco中AutoPtr的说明和注意事项
AutoPtr& assign(C* ptr) { if (_ptr != ptr) { if (_ptr) _ptr->release(); _ptr = ptr; } return *this; } AutoPtr& operator = (C* ptr) { return assign(ptr); }
Poco::AutoPtr与转换函数:
template <class Other> AutoPtr<Other> cast() const /// Casts the AutoPtr via a dynamic cast to the given type. /// Returns an AutoPtr containing NULL if the cast fails. /// Example: (assume class Sub: public Super) /// AutoPtr<Super> super(new Sub()); /// AutoPtr<Sub> sub = super.cast<Sub>(); /// poco_assert (sub.get()); { Other* pOther = dynamic_cast<Other*>(_ptr); return AutoPtr<Other>(pOther, true); }
template <class Other> AutoPtr(const AutoPtr<Other>& ptr): _ptr(const_cast<Other*>(ptr.get())) { if (_ptr) _ptr->duplicate(); } template <class Other> AutoPtr& assign(const AutoPtr<Other>& ptr) { if (ptr.get() != _ptr) { if (_ptr) _ptr->release(); _ptr = const_cast<Other*>(ptr.get()); if (_ptr) _ptr->duplicate(); } return *this; } template <class Other> AutoPtr& operator = (const AutoPtr<Other>& ptr) { return assign<Other>(ptr); }
注意事项和陷阱:
AutoPtr::AutoPtr(C* pObject, bool shared); AutoPtr& AutoPtr::assign(C* pObject, bool shared);其中shared值必须为true。下面是一个样例:
#include "Poco/AutoPtr.h" #include "Poco/RefCountedObject.h" class A: public Poco::RefCountedObject { }; int main(int argc, char** argv) { Poco::AutoPtr<A> p1(new A); A* pA = p1; // Poco::AutoPtr<A> p2(pA); // BAD! p2 assumes sole ownership Poco::AutoPtr<A> p2(pA, true); // Okay: p2 shares ownership with p1 Poco::AutoPtr<A> p3; // p3 = pA; // BAD! p3 assumes sole ownership p3.assign(pA, true); // Okay: p3 shares ownership with p1 return 0; }