文章目录
三、内存管理库
3.1 smart_ptr
3.1.1 RAII机制
为了管理内存资源,c++程序员经常采用RAII机制(资源获取即初始化),在使用资源的构造函数内申请资源,然后使用,最后在析构时释放资源。
在栈空间上申请的空间会在生命周期结束后自动调用析构,但是如果是new的对象则不会自动调用析构函数,只能在使用delete后才会释放空间,这里就存在内存泄漏的风险,如果意外导致delete语句未执行,将会产生不小的麻烦,这个内存将永久丢失了。
3.1.2 智能指针
智能指针会在退出作用域时候,不论正常流程离开还是异常流程离开,总会调用delete来释放资源。
smart_ptr库是对c++98的完美补充,其下有六种常见类型指针:
- scoped_ptr:
- scoped_array:
- shared_ptr:
- shared_array:
- weak_ptr:
- intrusive_ptr:
3.1.3 scoped_ptr
这是一个很类似于auto_ptr的指针,其所有权更加严格,不能转让,一旦其获取了对象的管理权,则无法从它那里取回对象的管理权(类似于unique_ptr)。这个指针只能在本作用域内使用,不接受转让。
-
源码:
template<class T> class scoped_ptr // noncopyable { private: T * px; scoped_ptr(scoped_ptr const &); scoped_ptr & operator=(scoped_ptr const &); typedef scoped_ptr<T> this_type; void operator==( scoped_ptr const& ) const;//私有的,不支持外部比较 void operator!=( scoped_ptr const& ) const; public: typedef T element_type; explicit scoped_ptr( T * p = 0 ) BOOST_SP_NOEXCEPT : px( p ) { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) boost::sp_scalar_constructor_hook( px ); #endif } #ifndef BOOST_NO_AUTO_PTR explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_SP_NOEXCEPT : px( p.release() ) { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) boost::sp_scalar_constructor_hook( px ); #endif } #endif ~scoped_ptr() BOOST_SP_NOEXCEPT { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) boost::sp_scalar_destructor_hook( px ); #endif boost::checked_delete( px ); } void reset(T * p = 0) BOOST_SP_NOEXCEPT_WITH_ASSERT { BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors this_type(p).swap(*this); } T & operator*() const BOOST_SP_NOEXCEPT_WITH_ASSERT { BOOST_ASSERT( px != 0 ); return *px; } T * operator->() const BOOST_SP_NOEXCEPT_WITH_ASSERT { BOOST_ASSERT( px != 0 ); return px; } T * get() const BOOST_SP_NOEXCEPT { return px; } // implicit conversion to "bool" #include <boost/smart_ptr/detail/operator_bool.hpp> void swap(scoped_ptr & b) BOOST_SP_NOEXCEPT { T * tmp = b.px; b.px = px; px = tmp; } };
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <string> using namespace std; using namespace boost; int main() { scoped_ptr<string> sp(new string("text")); cout << *sp<<endl; //内容 cout << sp->size()<<endl;//实际字节数 //scoped_ptr源码中将赋值和拷贝构造设置成了私有成员,即不能够进行拷贝和赋值 //除了*和->外,其没有重载其他的运算符,所以也不能进行++或者--操作 sp++;//错误的使用:未定义++操作符 scoped_ptr<string> sp2=sp;//错误的使用:隐式调用拷贝构造函数,因为私有所以不允许这样做 return 0; }
-
和auto_ptr的区别:
使用上几乎一样,auto_ptr允许转移代理,但是代理只能有一个(一旦通过赋值转移代理后,原auto_ptr对象将会失去管理权),可以用于函数传参,这个用意是好的,但是这个过程很危险。而scoped_ptr私有了拷贝构造和赋值,所以直接不允许这样做。
3.1.4 scoped_array
内部封装了new [],即弥补了标准库中没有可以指向数组的智能指针的缺陷。
比scoped_ptr多的是提供了[]的重载运算符,可以正常使用下标索引访问对象中的元素
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <string> #include <cstring> using namespace std; using namespace boost; int main() { scoped_array<int> sa(new int[10]); //memset(sa.get(),0,sizeof (int)*10);//get()方法得到该对象的指针。 fill_n(sa.get(),10,5);//也可以使用memset for(int i=0;i<10;i++){ cout<<sa[i]<<endl; } return 0; }
3.1.5 shared_ptr
shared_ptr 是一个最想指针的“智能指针”,是boost.smart_ptr中最具有价值的一个,也是最有用的。有着非常高的利用价值。
shared_ptr和scoped_ptr一样,封装了new操作符在堆上分配动态对象,但是它实现的是引用计数的只能指针。可以自由的进行拷贝和赋值,任意地方都可以共享它,当没有代码使用它时才会释放被包装的动态分配的对象。shared_ptr也可以安全的放到STL的标准容器中去,弥补了auto_ptr因为语义转移而不能放进容器中的缺陷。
shared_ptr也提供基本的线程安全保证,一个shared_ptr可以被多个线程安全读取,但其他的访问形式结果是未定义的。
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <string> #include <cstring> using namespace std; using namespace boost; int main() { boost::shared_ptr<int> sp(new int (0)); assert(sp); assert(sp.unique()); *sp = 23; boost::shared_ptr<int> sp2 = sp; assert(sp==sp2 && sp.use_count()==2); *sp2 = 100; assert(*sp2==100); cout<<*sp<<" "<<*sp2<<endl; sp.reset(); assert(!sp); cout<<sp<<endl;//sp为空指针 boost::shared_ptr<string> sps(new string("smart")); assert(sps->size()==5); return 0; }
-
shared_ptr下的自由工厂函数:
用来弥补不对称性(只new不delete),因为shared_ptr中封装了new操作符,但是过多调用也会有过多的new操作符的问题,所以才有了工厂函数。
该函数在<make_shared.hpp>中定义,声明在boost命名空间中,函数原型如下:
template< class T, class... Args > typename boost::detail::sp_if_not_array< T >::type make_shared( Args && ... args ) { boost::shared_ptr< T > pt( static_cast< T* >( 0 ), BOOST_SP_MSD( T ) ); boost::detail::sp_ms_deleter< T > * pd = static_cast<boost::detail::sp_ms_deleter< T > *>( pt._internal_get_untyped_deleter() ); void * pv = pd->address(); ::new( pv ) T( boost::detail::sp_forward<Args>( args )... ); pd->set_initialized(); T * pt2 = static_cast< T* >( pv ); boost::detail::sp_enable_shared_from_this( &pt, pt2, pt2 ); return boost::shared_ptr< T >( pt, pt2 ); }
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <boost/make_shared.hpp>//自用工厂函数的头文件 #include <string> #include <cstring> #include <vector> using namespace std; using namespace boost; int main() { boost::shared_ptr<string> sp = boost::make_shared<string>("make shared"); boost::shared_ptr<vector<int> > spv = boost::make_shared<vector<int> >(10, 2); return 0; }
-
除了make_shared(),smart_ptr还提供一个allocate_shared()函数,使用方法和上边一样,只是多了一个内存分配器参数。
-
-
应用于标准容器:存放标准指针的标准容器一般在资源释放的时候会有很多麻烦的问题,所以可以使用shared_ptr存放于标准容器中(shared_ptr支持拷贝和赋值,而auto_ptr 和 scoped_ptr不支持这样做。
#include <iostream> #include <boost/smart_ptr.hpp> #include <boost/make_shared.hpp>//自用工厂函数的头文件 #include <string> #include <cstring> #include <vector> using namespace std; using namespace boost; int main() { typedef vector<boost::shared_ptr<int> > vs; vs v(10); // 10个元素的vector int i = 0; for (vs::iterator it=v.begin();it!=v.end();++it) { (*it) = boost::make_shared<int>(++i); // 避免了new int(++i);让代码更加对称 cout<< *(*it) <<","; } cout<<endl; boost::shared_ptr<int> p = v[9]; (*p) = 100; cout << *(v[9])<<endl; return 0; }
-
应用于桥接模式:
桥接模式是一种结构型设计模式,其把类的具体实现模式对用户隐藏起来,已达到类之间最小耦合关系。桥接模式也被称之为pimpl或者handle/body惯用法。它可以将头文件的依赖关系降到最低,减小编译事件,而且可以不使用虚函数实现多态。
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过==提供抽象化和实现化之间的桥接结构,来实现二者的解耦==。
通常用相同的抽象方法但是不同的桥接实现类,来实现不同的实现功能。
桥接模式的意图:实现抽象部分和实现部分的分离。
-
桥接模式的实现:
我们有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircle、GreenCircle。Shape是一个抽象类,将使用 DrawAPI 的对象。BridgePatternDemo 类使用 Shape 类来画出不同颜色的圆。
-
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <boost/make_shared.hpp>//自用工厂函数的头文件 #include <string> #include <cstring> #include <vector> using namespace std; using namespace boost; class sample{ private: class impl; boost::shared_ptr<impl> p; public: sample(); void print(); }; class sample::impl{ public: void print(){cout<<"impl print"<<endl;} }; sample::sample():p(new impl){}//不用管理impl的内部释放了 void sample::print(){p->print();} int main() { sample sm; sm.print(); return 0; }
3.1.6 weak_ptr
是为了配合shared_ptr而产生的一种智能指针,但是其没有重载->和*运算符,所以其不具备指针的能力。其最大的作用是协助shared_ptr,成为一个旁观者观察资源的使用情况。
weak_ptr被设计和shared_ptr一起工作,可以从一个shared_ptr或者weak_ptr构造得到,获得资源的观测权,它的构造不会引起引用计数的增加。其中的use_count()函数可以观测引用个数,另一个成员函数expired()功能等价于use_count()==0,引用计数为0(没有指针指向资源或者资源被释放)时返回true,但是更快,表示被观测的资源不复存在。它可以通过lock()函数获得一个可用的shared_ptr对象,从而操作资源,当expired()==true时,lock()函数将返回一个存储空指针的shared_ptr对象。
-
原型:
template<class T> class weak_ptr { private: // Borland 5.5.1 specific workarounds typedef weak_ptr<T> this_type; public: typedef typename boost::detail::sp_element< T >::type element_type; BOOST_CONSTEXPR weak_ptr() BOOST_SP_NOEXCEPT : px(0), pn() { } // generated copy constructor, assignment, destructor are fine... #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) // ... except in C++0x, move disables the implicit copy weak_ptr( weak_ptr const & r ) BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn ) { } weak_ptr & operator=( weak_ptr const & r ) BOOST_SP_NOEXCEPT { px = r.px; pn = r.pn; return *this; } #endif // // The "obvious" converting constructor implementation: // // template<class Y> // weak_ptr(weak_ptr<Y> const & r): px(r.px), pn(r.pn) // { // } // // has a serious problem. // // r.px may already have been invalidated. The px(r.px) // conversion may require access to *r.px (virtual inheritance). // // It is not possible to avoid spurious access violations since // in multithreaded programs r.px may be invalidated at any point. // template<class Y> #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( weak_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() ) #else weak_ptr( weak_ptr<Y> const & r ) #endif BOOST_SP_NOEXCEPT : px(r.lock().get()), pn(r.pn) { boost::detail::sp_assert_convertible< Y, T >(); } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template<class Y> #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( weak_ptr<Y> && r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() ) #else weak_ptr( weak_ptr<Y> && r ) #endif BOOST_SP_NOEXCEPT : px( r.lock().get() ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) { boost::detail::sp_assert_convertible< Y, T >(); r.px = 0; } // for better efficiency in the T == Y case weak_ptr( weak_ptr && r ) BOOST_SP_NOEXCEPT : px( r.px ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) { r.px = 0; } // for better efficiency in the T == Y case weak_ptr & operator=( weak_ptr && r ) BOOST_SP_NOEXCEPT { this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); return *this; } #endif template<class Y> #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() ) #else weak_ptr( shared_ptr<Y> const & r ) #endif BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn ) { boost::detail::sp_assert_convertible< Y, T >(); } // aliasing template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) BOOST_SP_NOEXCEPT: px( p ), pn( r.pn ) { } template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) BOOST_SP_NOEXCEPT: px( p ), pn( r.pn ) { } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) BOOST_SP_NOEXCEPT: px( p ), pn( std::move( r.pn ) ) { } #endif #if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1300) template<class Y> weak_ptr & operator=( weak_ptr<Y> const & r ) BOOST_SP_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.lock().get(); pn = r.pn; return *this; } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template<class Y> weak_ptr & operator=( weak_ptr<Y> && r ) BOOST_SP_NOEXCEPT { this_type( static_cast< weak_ptr<Y> && >( r ) ).swap( *this ); return *this; } #endif template<class Y> weak_ptr & operator=( shared_ptr<Y> const & r ) BOOST_SP_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.px; pn = r.pn; return *this; } #endif shared_ptr<T> lock() const BOOST_SP_NOEXCEPT { return shared_ptr<T>( *this, boost::detail::sp_nothrow_tag() ); } long use_count() const BOOST_SP_NOEXCEPT { return pn.use_count(); } bool expired() const BOOST_SP_NOEXCEPT { return pn.use_count() == 0; } bool _empty() const BOOST_SP_NOEXCEPT // extension, not in std::weak_ptr { return pn.empty(); } bool empty() const BOOST_SP_NOEXCEPT // extension, not in std::weak_ptr { return pn.empty(); } void reset() BOOST_SP_NOEXCEPT { this_type().swap(*this); } void swap(this_type & other) BOOST_SP_NOEXCEPT { std::swap(px, other.px); pn.swap(other.pn); } template<class Y> bool owner_before( weak_ptr<Y> const & rhs ) const BOOST_SP_NOEXCEPT { return pn < rhs.pn; } template<class Y> bool owner_before( shared_ptr<Y> const & rhs ) const BOOST_SP_NOEXCEPT { return pn < rhs.pn; } // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. (Matthew Langston) #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: template<class Y> friend class weak_ptr; template<class Y> friend class shared_ptr; #endif element_type * px; // contained pointer boost::detail::weak_count pn; // reference counter };
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <boost/make_shared.hpp>//自用工厂函数的头文件 #include <string> #include <cstring> #include <vector> using namespace std; using namespace boost; int main() { boost::shared_ptr<int> p = boost::make_shared<int>(10); assert(p.use_count()==1); boost::weak_ptr<int> wp(p); assert(wp.use_count()==1); if(!wp.expired()){//当资源未被释放时 boost::shared_ptr<int> sp = wp.lock(); *sp = 100; assert(wp.use_count()==2); } assert(wp.use_count()==1); p.reset(); assert(wp.expired());//有资源时报错 assert(!wp.lock());//非空指针报错 return 0; }
3.2 pool
内存池:预先分配一块很大的内存,通过某些算法实现高效的自定制内存分配。
pool是最简单最有用的内存池类,可以返回一个简单数据类型POD的内存指针。
-
原型:
template <typename UserAllocator> class pool: protected simple_segregated_storage < typename UserAllocator::size_type > { public: typedef UserAllocator user_allocator; //!< User allocator. typedef typename UserAllocator::size_type size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated. typedef typename UserAllocator::difference_type difference_type; //!< A signed integral type that can represent the difference of any two pointers. private: BOOST_STATIC_CONSTANT(size_type, min_alloc_size = (::boost::integer::static_lcm<sizeof(void *), sizeof(size_type)>::value) ); BOOST_STATIC_CONSTANT(size_type, min_align = (::boost::integer::static_lcm< ::boost::alignment_of<void *>::value, ::boost::alignment_of<size_type>::value>::value) ); //! \returns 0 if out-of-memory. //! Called if malloc/ordered_malloc needs to resize the free list. void * malloc_need_resize(); //! Called if malloc needs to resize the free list. void * ordered_malloc_need_resize(); //! Called if ordered_malloc needs to resize the free list. protected: details::PODptr<size_type> list; //!< List structure holding ordered blocks. simple_segregated_storage<size_type> & store() { //! \returns pointer to store. return *this; } const simple_segregated_storage<size_type> & store() const { //! \returns pointer to store. return *this; } const size_type requested_size; size_type next_size; size_type start_size; size_type max_size; //! finds which POD in the list 'chunk' was allocated from. details::PODptr<size_type> find_POD(void * const chunk) const; // is_from() tests a chunk to determine if it belongs in a block. static bool is_from(void * const chunk, char * const i, const size_type sizeof_i) { //! \param chunk chunk to check if is from this pool. //! \param i memory chunk at i with element sizeof_i. //! \param sizeof_i element size (size of the chunk area of that block, not the total size of that block). //! \returns true if chunk was allocated or may be returned. //! as the result of a future allocation. //! //! Returns false if chunk was allocated from some other pool, //! or may be returned as the result of a future allocation from some other pool. //! Otherwise, the return value is meaningless. //! //! Note that this function may not be used to reliably test random pointer values. // We use std::less_equal and std::less to test 'chunk' // against the array bounds because standard operators // may return unspecified results. // This is to ensure portability. The operators < <= > >= are only // defined for pointers to objects that are 1) in the same array, or // 2) subobjects of the same object [5.9/2]. // The functor objects guarantee a total order for any pointer [20.3.3/8] std::less_equal<void *> lt_eq; std::less<void *> lt; return (lt_eq(i, chunk) && lt(chunk, i + sizeof_i)); } size_type alloc_size() const { //! Calculated size of the memory chunks that will be allocated by this Pool. //! \returns allocated size. // For alignment reasons, this used to be defined to be lcm(requested_size, sizeof(void *), sizeof(size_type)), // but is now more parsimonious: just rounding up to the minimum required alignment of our housekeeping data // when required. This works provided all alignments are powers of two. size_type s = (std::max)(requested_size, min_alloc_size); size_type rem = s % min_align; if(rem) s += min_align - rem; BOOST_ASSERT(s >= min_alloc_size); BOOST_ASSERT(s % min_align == 0); return s; } static void * & nextof(void * const ptr) { //! \returns Pointer dereferenced. //! (Provided and used for the sake of code readability :) return *(static_cast<void **>(ptr)); } public: // pre: npartition_size != 0 && nnext_size != 0 explicit pool(const size_type nrequested_size, const size_type nnext_size = 32, const size_type nmax_size = 0) : list(0, 0), requested_size(nrequested_size), next_size(nnext_size), start_size(nnext_size),max_size(nmax_size) { //! Constructs a new empty Pool that can be used to allocate chunks of size RequestedSize. //! \param nrequested_size Requested chunk size //! \param nnext_size parameter is of type size_type, //! is the number of chunks to request from the system //! the first time that object needs to allocate system memory. //! The default is 32. This parameter may not be 0. //! \param nmax_size is the maximum number of chunks to allocate in one block. } ~pool() { //! Destructs the Pool, freeing its list of memory blocks. purge_memory(); } // Releases memory blocks that don't have chunks allocated // pre: lists are ordered // Returns true if memory was actually deallocated bool release_memory(); // Releases *all* memory blocks, even if chunks are still allocated // Returns true if memory was actually deallocated bool purge_memory(); size_type get_next_size() const { //! Number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be 0. //! \returns next_size; return next_size; } void set_next_size(const size_type nnext_size) { //! Set number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be set to 0. //! \returns nnext_size. next_size = start_size = nnext_size; } size_type get_max_size() const { //! \returns max_size. return max_size; } void set_max_size(const size_type nmax_size) { //! Set max_size. max_size = nmax_size; } size_type get_requested_size() const { //! \returns the requested size passed into the constructor. //! (This value will not change during the lifetime of a Pool object). return requested_size; } // Both malloc and ordered_malloc do a quick inlined check first for any // free chunks. Only if we need to get another memory block do we call // the non-inlined *_need_resize() functions. // Returns 0 if out-of-memory void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() { //! Allocates a chunk of memory. Searches in the list of memory blocks //! for a block that has a free chunk, and returns that free chunk if found. //! Otherwise, creates a new memory block, adds its free list to pool's free list, //! \returns a free chunk from that block. //! If a new memory block cannot be allocated, returns 0. Amortized O(1). // Look for a non-empty storage if (!store().empty()) return (store().malloc)(); return malloc_need_resize(); } void * ordered_malloc() { //! Same as malloc, only merges the free lists, to preserve order. Amortized O(1). //! \returns a free chunk from that block. //! If a new memory block cannot be allocated, returns 0. Amortized O(1). // Look for a non-empty storage if (!store().empty()) return (store().malloc)(); return ordered_malloc_need_resize(); } // Returns 0 if out-of-memory // Allocate a contiguous section of n chunks void * ordered_malloc(size_type n); //! Same as malloc, only allocates enough contiguous chunks to cover n * requested_size bytes. Amortized O(n). //! \returns a free chunk from that block. //! If a new memory block cannot be allocated, returns 0. Amortized O(1). // pre: 'chunk' must have been previously // returned by *this.malloc(). void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk) { //! Deallocates a chunk of memory. Note that chunk may not be 0. O(1). //! //! Chunk must have been previously returned by t.malloc() or t.ordered_malloc(). //! Assumes that chunk actually refers to a block of chunks //! spanning n * partition_sz bytes. //! deallocates each chunk in that block. //! Note that chunk may not be 0. O(n). (store().free)(chunk); } // pre: 'chunk' must have been previously // returned by *this.malloc(). void ordered_free(void * const chunk) { //! Same as above, but is order-preserving. //! //! Note that chunk may not be 0. O(N) with respect to the size of the free list. //! chunk must have been previously returned by t.malloc() or t.ordered_malloc(). store().ordered_free(chunk); } // pre: 'chunk' must have been previously // returned by *this.malloc(n). void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunks, const size_type n) { //! Assumes that chunk actually refers to a block of chunks. //! //! chunk must have been previously returned by t.ordered_malloc(n) //! spanning n * partition_sz bytes. //! Deallocates each chunk in that block. //! Note that chunk may not be 0. O(n). const size_type partition_size = alloc_size(); const size_type total_req_size = n * requested_size; const size_type num_chunks = total_req_size / partition_size + ((total_req_size % partition_size) ? true : false); store().free_n(chunks, num_chunks, partition_size); } // pre: 'chunk' must have been previously // returned by *this.malloc(n). void ordered_free(void * const chunks, const size_type n) { //! Assumes that chunk actually refers to a block of chunks spanning n * partition_sz bytes; //! deallocates each chunk in that block. //! //! Note that chunk may not be 0. Order-preserving. O(N + n) where N is the size of the free list. //! chunk must have been previously returned by t.malloc() or t.ordered_malloc(). const size_type partition_size = alloc_size(); const size_type total_req_size = n * requested_size; const size_type num_chunks = total_req_size / partition_size + ((total_req_size % partition_size) ? true : false); store().ordered_free_n(chunks, num_chunks, partition_size); } // is_from() tests a chunk to determine if it was allocated from *this bool is_from(void * const chunk) const { //! \returns Returns true if chunk was allocated from u or //! may be returned as the result of a future allocation from u. //! Returns false if chunk was allocated from some other pool or //! may be returned as the result of a future allocation from some other pool. //! Otherwise, the return value is meaningless. //! Note that this function may not be used to reliably test random pointer values. return (find_POD(chunk).valid()); } };
-
案例:
#include <iostream> #include <boost/pool/pool.hpp> using namespace std; using namespace boost; int main() { pool<> p(sizeof(int));//整型字节的空间//<>是因为采用默认模板 int* p1 = (int*)p.malloc();//malloc函数分配内存失败后会返回空指针,一般返回void* if(!p1){ cout<<"error!"<<endl; }else{ cout<<p.get_requested_size()<<endl; } assert(p.is_from(p1));//p1是否是p所申请的内存指针 p.free(p1);//释放p1 for(int i=0;i<10;i++){ p.ordered_malloc(10);//连续申请10字节空间 } return 0;//内存池对象析构,所有空间被释放 }
- 注意:pool只能申请简单类型的空间,不能生成复杂类型的空间,因为其只申请空间,并不会调用构造函数。如果要分配复杂类型的空间,要是用object_pool();
3.3 object_pool
object_pool是pool的受保护继承的子类,因此外界无法使用pool的接口,而object_pool的特殊之处在于construct()函数和destroy()函数,是其价值所在。construct()实际上是一组函数,有三种重载形式,它先调用malloc分配空间,然后再通过参数调用类的构造函数,返回一个已经初始化的对象的指针。而destroy用于先调用对象的析构函数,再调用free()释放内存。
-
原型:
template <typename T, typename UserAllocator> class object_pool: protected pool<UserAllocator> { //! public: typedef T element_type; //!< ElementType typedef UserAllocator user_allocator; //!< typedef typename pool<UserAllocator>::size_type size_type; //!< pool<UserAllocator>::size_type typedef typename pool<UserAllocator>::difference_type difference_type; //!< pool<UserAllocator>::difference_type protected: //! \return The underlying boost:: \ref pool storage used by *this. pool<UserAllocator> & store() { return *this; } //! \return The underlying boost:: \ref pool storage used by *this. const pool<UserAllocator> & store() const { return *this; } // for the sake of code readability :) static void * & nextof(void * const ptr) { //! \returns The next memory block after ptr (for the sake of code readability :) return *(static_cast<void **>(ptr)); } public: explicit object_pool(const size_type arg_next_size = 32, const size_type arg_max_size = 0) : pool<UserAllocator>(sizeof(T), arg_next_size, arg_max_size) { //! Constructs a new (empty by default) ObjectPool. //! \param next_size Number of chunks to request from the system the next time that object needs to allocate system memory (default 32). //! \pre next_size != 0. //! \param max_size Maximum number of chunks to ever request from the system - this puts a cap on the doubling algorithm //! used by the underlying pool. } ~object_pool(); // Returns 0 if out-of-memory. element_type * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() { //! Allocates memory that can hold one object of type ElementType. //! //! If out of memory, returns 0. //! //! Amortized O(1). return static_cast<element_type *>(store().ordered_malloc()); } void free BOOST_PREVENT_MACRO_SUBSTITUTION(element_type * const chunk) { //! De-Allocates memory that holds a chunk of type ElementType. //! //! Note that p may not be 0.\n //! //! Note that the destructor for p is not called. O(N). store().ordered_free(chunk); } bool is_from(element_type * const chunk) const { /*! \returns true if chunk was allocated from *this or may be returned as the result of a future allocation from *this. Returns false if chunk was allocated from some other pool or may be returned as the result of a future allocation from some other pool. Otherwise, the return value is meaningless. \note This function may NOT be used to reliably test random pointer values! */ return store().is_from(chunk); } element_type * construct() { //! \returns A pointer to an object of type T, allocated in memory from the underlying pool //! and default constructed. The returned objected can be freed by a call to \ref destroy. //! Otherwise the returned object will be automatically destroyed when *this is destroyed. element_type * const ret = (malloc)(); if (ret == 0) return ret; try { new (ret) element_type(); } catch (...) { (free)(ret); throw; } return ret; } #if defined(BOOST_DOXYGEN) template <class Arg1, ... class ArgN> element_type * construct(Arg1&, ... ArgN&) { //! \returns A pointer to an object of type T, allocated in memory from the underlying pool //! and constructed from arguments Arg1 to ArgN. The returned objected can be freed by a call to \ref destroy. //! Otherwise the returned object will be automatically destroyed when *this is destroyed. //! //! \note Since the number and type of arguments to this function is totally arbitrary, a simple system has been //! set up to automatically generate template construct functions. This system is based on the macro preprocessor //! m4, which is standard on UNIX systems and also available for Win32 systems.\n\n //! detail/pool_construct.m4, when run with m4, will create the file detail/pool_construct.ipp, which only defines //! the construct functions for the proper number of arguments. The number of arguments may be passed into the //! file as an m4 macro, NumberOfArguments; if not provided, it will default to 3.\n\n //! For each different number of arguments (1 to NumberOfArguments), a template function is generated. There //! are the same number of template parameters as there are arguments, and each argument's type is a reference //! to that (possibly cv-qualified) template argument. Each possible permutation of the cv-qualifications is also generated.\n\n //! Because each permutation is generated for each possible number of arguments, the included file size grows //! exponentially in terms of the number of constructor arguments, not linearly. For the sake of rational //! compile times, only use as many arguments as you need.\n\n //! detail/pool_construct.bat and detail/pool_construct.sh are also provided to call m4, defining NumberOfArguments //! to be their command-line parameter. See these files for more details. } #else // Include automatically-generated file for family of template construct() functions. // Copy .inc renamed .ipp to conform to Doxygen include filename expectations, PAB 12 Jan 11. // But still get Doxygen warning: // I:/boost-sandbox/guild/pool/boost/pool/object_pool.hpp:82: // Warning: include file boost/pool/detail/pool_construct.ipp // not found, perhaps you forgot to add its directory to INCLUDE_PATH? // But the file IS found and referenced OK, but cannot view code. // This seems because not at the head of the file // But if moved this up, Doxygen is happy, but of course it won't compile, // because the many constructors *must* go here. #ifndef BOOST_NO_TEMPLATE_CV_REF_OVERLOADS # include <boost/pool/detail/pool_construct.ipp> #else # include <boost/pool/detail/pool_construct_simple.ipp> #endif #endif void destroy(element_type * const chunk) { //! Destroys an object allocated with \ref construct. //! //! Equivalent to: //! //! p->~ElementType(); this->free(p); //! //! \pre p must have been previously allocated from *this via a call to \ref construct. chunk->~T(); (free)(chunk); } size_type get_next_size() const { //! \returns The number of chunks that will be allocated next time we run out of memory. return store().get_next_size(); } void set_next_size(const size_type x) { //! Set a new number of chunks to allocate the next time we run out of memory. //! \param x wanted next_size (must not be zero). store().set_next_size(x); } };
-
案例:
#include <iostream> #include <boost/smart_ptr.hpp> #include <boost/make_shared.hpp>//自用工厂函数的头文件 #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> #include <string> #include <cstring> #include <vector> using namespace std; using namespace boost; struct demo_class{ int a,b,c; demo_class(int x=1,int y=2,int z=3):a(x),b(y),c(z){} }; int main() { object_pool<demo_class> p1; demo_class* p = p1.malloc(); assert(p1.is_from(p)); assert(p->a!=1 || p->b!=2 || p->c!=3);//p所指向的空间并没有初始化 std::cout<<"未初始化的三个值:"<< p->a <<" "<< p->b <<" "<< p->c<<std::endl; p = p1.construct();//调用demo_class的构造函数,采用默认值进行初始化。(x==1,y==2,z==3) assert(p->a==1 || p->b==2 || p->c==3);//p所指的空间被初始化 cout<< p->a << p->b << p->c<<endl; return 0;//内存池对象析构,所有空间被释放 }