boost 内存管理学习笔记1
本帖最后由 元始天尊 于 2015-2-24 15:41 编辑学BOOST和STL对于做应用层软件十分重要,相信不用上述2个库的C++应用软件,在未来会因为功能维护、测试、安全性、重构等问题而慢慢退出历史舞台。
简单说下boost使用,我下的是最新版boost1.57的7z格式50M,解压出来400M,VS2013中设置包含文件目录和库目录为boost下的Include和lib即可使用。当然用的时候要添加头文件了。
C++中可以做到完全不用指针,也不会出现内存泄漏/野指针/访问越界等问题,而JAVA,C#等语言有垃圾回收机制,虽然C++ 标准提供了auto_ptr但是并不能解决所有问题。不光是指针,资源也包括文件描述符/socket/handle/数据库等。申请忘记释放会产生未知后果。为了方便管理资源,C++程序员通常采用RAII机制(资源获取即初始化),即类构造时申请资源,类析构时释放资源。这分为2种情况:栈对象离开作用域后会自动析构没有问题,而堆对象则需手动释放,也就存在资源泄露隐患,这个可能是逻辑复杂而忘记delete或者异常导致未执行delete。即使熟练的程序员也可能未能正确使用delete。
智能指针作用:减少因错误操作指针引起的内存泄漏/野指针/访问越界的发生,避免因异常导致程序流程的改变(不执行delete)而到处添加异常捕获代码以释放资源的劳动。正确对待指针的做法是return之前调用delete并使用try捕获所有异常,在catch中调用delete。
标准中的auto_ptr被广泛使用,然而无法做到引用计数型智能指针。boost.smart_ptr是C++98的绝佳补充,提供了6种智能指针:scoped_ptr,scoped_array,shared_ptr,shared_array,weak_ptr,intrusive_ptr,其中shared_ptr和weak_ptr已被收入C++新标准中。他们都是轻量级对象,速度和原始指针相差不大,对于类型T也有个基本要求:~T()不能抛出异常。
先来看scoped_ptr
template<class T>
class scoped_ptr
{
private:
T* px;
scoped_ptr(scoped_ptr const&);//禁止拷贝,转让所有权
scoped_ptr& operator=(scoped_ptr const&);//禁止拷贝,转让所有权
public:
explicit scoped_ptr(T* p=0);//禁止scoped_ptr p=q形式的隐式转换,强制scoped_ptr p(q)形式
~scoped_ptr();
void reset(T* p=0);
T& operator*() const;
T* operator->() const;
T* get() const;
operator unspecified-bool-type() const;
void swap(scoped_ptr& b);
};
例:
#include <boost/scoped_ptr.hpp>
#include <iostream>
struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; }
int add_one() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<Shoe> x(new Shoe);
MyClass my_instance;
std::cout << my_instance.add_one() << '\n';
std::cout << my_instance.add_one() << '\n';
}
输出
1
2
Buckle my shoe
《Boost程序库完全开发指南:深入C++准标准库》中说道:
scoped_ptr是针对单个元素对象的,不支持转让所有权,不支持比较操作,不支持拷贝、赋值,不能作为容器的元素(不支持拷贝和赋值),
reset函数会删除原来的指针,再保存新指针值,如果新指针为空则scoped_ptr不持有任何指针,一般情况下reset不应该被调用——资源应该自动管理。
不过我还是很好奇reset在大师们的手里是如何实现的:
typedef scoped_ptr<T> this_type;
void reset(T * p = 0) // never throws
{
BOOST_ASSERT( p == 0 || p != px ); // 自身不能重复赋值
this_type(p).swap(*this);
}
void swap(scoped_ptr & b) BOOST_NOEXCEPT
{
T * tmp = b.px;
b.px = px;
px = tmp;
}
和某些书上讲的高级指针操作一样巧妙,reset里定义了一个临时对象 this_type(p),等价于scoped_ptr<T> newobj(p),并进行swap,这里巧妙之处在于把要释放的指针(这里为原this->px=旧指针po),交换临时对象里的(newobj->px=新指针pn),交换之后自然原this->px为新指针pn,而临时对象newobj->px为要释放的指针,newobj是局部对象,reset结束后即释放,自动调用析构函数释放旧指针po。能看到这一层就说明C++基础学懂了。。。。。
之所以reset要用swap实现除了避免异常导致资源泄漏外,同时提供了交换指针的实现,一举两得。
问题:为何常用的智能指针都是管理堆对象,而不管理栈对象?
前面已经说过,栈对象在作用域结束后自动释放,因此不需要操心,而堆对象必须手动释放因此需要额外管理
问题:为何支持第一种,而不支持第二种?例:
scoped_ptr ptr1(p1),ptr2(p2);
if(ptr1 == ptr2){}//编译失败
if(ptr1 == 0){}//编译成功
相当于operator()参数一个是scoped_ptr&,另一个是0,怎么做到区分这2种操作的?
经过研究发现2处定义:
class scoped_ptr
{
private:
void operator==( scoped_ptr const& ) const;
}
template<class T> inline bool operator==( scoped_ptr<T> const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT
{
return p.get() == 0;
}
==操作先要匹配类内操作符重载函数,再匹配全局操作符重载函数,类内操作符重载函数总是匹配失败,因为是Private的,而类外匹配ptr1==ptr2的情况,传入参数是ptr2,不属于boost::detail::sp_nullptr_t类型,而ptr1==0,0属于boost::detail::sp_nullptr_t类型。
看一下定义:typedef std::nullptr_t sp_nullptr_t; std::nullptr_t是空指针类型,C++11引入的
再来看scoped_array
namespace boost {
template<class T> class scoped_array : noncopyable {
public:
typedef T element_type;
explicit scoped_array(T * p = 0); // never throws
~scoped_array(); // never throws
void reset(T * p = 0); // never throws
T & operator[](std::ptrdiff_t i) const; // never throws
T * get() const; // never throws
operator unspecified-bool-type() const; // never throws
void swap(scoped_array & b); // never throws
};
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b); // never throws
}
类似于scoped_ptr,不同的是它用来处理堆数组,而不是单个堆元素,数组操作和指针操作稍有不同,指针操作需要重载->和*,而数组需要重载[]
其他特性等同于scoped_ptr,例:
#include <boost/smart_ptr.hpp>
#include <algorithm>
using namespace boost;
using namespace std;
int main()
{
int *arr = new int;
scoped_array<int> sa(arr);
sa = sa + sa;
}
在使用动态数组时,不推荐scoped_array而推荐使用std::vector,它提支持动态增长,支持迭代器,支持STL算法
再来看shared_ptr,他是最重要的智能指针:
namespace boost {
class bad_weak_ptr: public std::exception;
template<class T> class weak_ptr;
template<class T> class shared_ptr {
public:
typedef see below element_type;
shared_ptr(); // never throws
shared_ptr(std::nullptr_t); // never throws
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
~shared_ptr(); // never throws
shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
shared_ptr(shared_ptr && r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> && r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(shared_ptr const & r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
shared_ptr & operator=(shared_ptr const && r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r); // never throws
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(std::nullptr_t); // never throws
void reset(); // never throws
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p); // never throws
T & operator*() const; // never throws; only valid when T is not an array type
T * operator->() const; // never throws; only valid when T is not an array type
element_type & operator[](std::ptrdiff_t i) const; // never throws; only valid when T is an array type
element_type * get() const; // never throws
bool unique() const; // never throws
long use_count() const; // never throws
explicit operator bool() const; // never throws
void swap(shared_ptr & b); // never throws
template<class Y> bool owner_before(shared_ptr<Y> const & rhs) const; // never throws
template<class Y> bool owner_before(weak_ptr<Y> const & rhs) const; // never throws
};
}
shared_ptr实现的是引用计数型的智能指针,可以自由拷贝和赋值,可以作为迭代器,在任意地方共享,直到没有代码使用它时,此时引用计数为0,删除包装对象。
也就是说,shared_ptr支持拷贝,赋值,比较等操作。 本帖最后由 元始天尊 于 2015-2-24 20:46 编辑
shared_ptr更灵活,如下例:
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <vector>
#include <iostream>
using namespace boost;
using namespace std;
#include <malloc.h>
class abstract
{
public:
virtual void f() = 0;
protected:
virtual ~abstract(){};
};
class impl :public abstract
{
public:
virtual void f(){ cout << "f" << endl; };
};
void deleter(impl* ptr)
{
cout << "deleter" << endl;
}
boost::shared_ptr<abstract> create()
{
return boost::shared_ptr<abstract>(new impl);
}
int main()
{
//应用于标准容器
vector<boost::shared_ptr<int>> v(10);
int i = 0;
for (vector<boost::shared_ptr<int>>::iterator pos = v.begin(); pos != v.end(); ++pos)
{
(*pos) = boost::make_shared<int>(++i);
cout << *(*pos) << ", ";
}
//应用于桥接模式
class sample
{
private:
class impl
{
public:
void print(){ cout << "bridge" << endl; };
};
boost::shared_ptr<impl> p;
public:
void print(){ p->print(); };//交给impl去做
};
boost::shared_ptr<sample>()->print();
//应用于工厂模式
create()->f();
//删除器
boost::shared_ptr<impl> pp(new impl, deleter);
}
shared_array和scoped_array一样不推荐使用
weak_array配合shared_array,提供use_count函数观测后者引用计数,没有重载*和->,提供lock获得资源引用进行操作
shared_array和scoped_array一样不推荐使用
weak_array配合shared_array,提供use_count函数观测后者引用计数,没有重载*和->,提供lock获得资源引用进行操作
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <vector>
#include <iostream>
using namespace boost;
using namespace std;
int main()
{
boost::shared_ptr<int> sp(new int(10));
boost::weak_ptr<int> wp(sp);
if (!wp.expired())
{
boost::shared_ptr<int> sp2 = wp.lock();
assert(wp.use_count() == 2);
}
}
weak_ptr可以让类产生的this指针自动管理自身
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <vector>
#include <iostream>
using namespace boost;
using namespace std;
class test :public boost::enable_shared_from_this<test>
{
public:
void printf(){ cout << "self_shared!" << endl; }
};
int main()
{
boost::shared_ptr<test> sp = boost::make_shared<test>();
sp->printf();
boost::shared_ptr<test> p = sp->shared_from_this();
p->printf();
}
boost内存管理学习笔记2
1.什么是内存池?
Pool allocation is a memory allocation scheme that is very fast, but limited in its usage.
池是一种内存分配机制,可以在某些条件下快速分配内存。
2.为什么要使用内存池?
Using Pools gives you more control over how memory is used in your program. For example, you could have a situation where you want to allocate a bunch of small objects at one point, and then reach a point in your program where none of them are needed any more. Using pool interfaces, you can choose to run their destructors or just drop them off into oblivion; the pool interface will guarantee that there are no system memory leaks.
使用池可以更好地管理程序内存使用,例如,当你想在某处代码中分配一小块对象,而到达另一处代码时不再需要这块内存,就可以使用池。使用池接口后,可以运行析构函数或者直接无视他们,池接口会保证不会产生内存泄露。
3.什么时候需要使用内存池?
Pools are generally used when there is a lot of allocation and deallocation of small objects. Another common usage is the situation above, where many objects may be dropped out of memory.
In general, use Pools when you need a more efficient way to do unusual memory control.
池通常用于处理分配释放大量小对象,或者如上面那种情况,很多对象不再使用,不需要存在于内存。通常用池可以更高效地控制内存。
4.怎样选择池分配器?
pool_allocator is a more general-purpose solution, geared towards efficiently servicing requests for any number of contiguous chunks.
fast_pool_allocator is also a general-purpose solution but is geared towards efficiently servicing requests for one chunk at a time; it will work for contiguous chunks, but not as well as pool_allocator.
If you are seriously concerned about performance, use fast_pool_allocator when dealing with containers such as std::list, and use pool_allocator when dealing with containers such as std::vector.
pool_allocator是最常见的方式,可以高效处理任意数量连续数据块分配请求,fast_pool_allocator也较为通用,适用于每次分配一块内存,分配连续块没有pool_allocator性能好。如果非常关注性能,可以在std::list类容器中使用fast_pool_allocator,std::vector类容器中使用pool_allocator
pool用于普通数据类型分配,原型:
struct default_user_allocator_new_delete
{
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char * malloc(const size_type bytes)
{ return new (std::nothrow) char; }
static void free(char * const block)
{ delete [] block; }
};
struct default_user_allocator_malloc_free
{
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char * malloc(const size_type bytes)
{ return reinterpret_cast<char *>(std::malloc(bytes)); }
static void free(char * const block)
{ std::free(block); }
};
template <typename UserAllocator = default_user_allocator_new_delete>
class pool
{
private:
pool(const pool &);
void operator=(const pool &);
public:
typedef UserAllocator user_allocator;
typedef typename UserAllocator::size_type size_type;
typedef typename UserAllocator::difference_type difference_type;
explicit pool(size_type requested_size);
~pool();
bool release_memory();
bool purge_memory();
bool is_from(void * chunk) const;
size_type get_requested_size() const;
void * malloc();
void * ordered_malloc();
void * ordered_malloc(size_type n);
void free(void * chunk);
void ordered_free(void * chunk);
void free(void * chunks, size_type n);
void ordered_free(void * chunks, size_type n);
};
void func()
{
boost::pool<> p(sizeof(int));
for (int i = 0; i < 10000; ++i)
{
int * const t = p.malloc();
... // Do something with t; don't take the time to free() it.
}
} // on function exit, p is destroyed, and all malloc()'ed ints are implicitly freed.
对象池object_pool用于分配对象,和普通类型不同支出在于自动构造析构,原型:
template <typename ElementType, typename UserAllocator = default_user_allocator_new_delete>
class object_pool
{
private:
object_pool(const object_pool &);
void operator=(const object_pool &);
public:
typedef ElementType element_type;
typedef UserAllocator user_allocator;
typedef typename pool<UserAllocator>::size_type size_type;
typedef typename pool<UserAllocator>::difference_type difference_type;
object_pool();
~object_pool();
element_type * malloc();
void free(element_type * p);
bool is_from(element_type * p) const;
element_type * construct();
// other construct() functions
void destroy(element_type * p);
};
void func()
{
boost::object_pool<X> p;
for (int i = 0; i < 10000; ++i)
{
X * const t = p.malloc();
... // Do something with t; don't take the time to free() it.
}
} // on function exit, p is destroyed, and all destructors for the X objects are called.
单例池,加锁池,确保全局只有一个singleton_pool对象p,有以下特点:
线程安全,所有访问p的行为同步化
确保p使用前已经构造
typedef boost::singleton_pool<MyPoolTag, sizeof(int)> my_pool;
void func()
{
for (int i = 0; i < 10000; ++i)
{
int * const t = my_pool::malloc();
... // Do something with t; don't take the time to free() it.
}
// Explicitly free all malloc()'ed ints.
my_pool::purge_memory();
}
页:
[1]