使用Boost进行资源管理
2017-03-30 10:19
190 查看
以下代码来源于《深入实践Boost:Boost程序库开发的94个秘笈》一书
以上代码看似正确,但若some_function1()或some_function2()抛出一个异常,p就不会被删除,当然,也可以通过增加try{}catch{}块来处理,但是代码会难看且难读。
Boost.SmartPtr库中提供boost::scoped_ptr类,有更好的解法:
boost::scoped_ptr的工作原理:
在析构函数钟,Boost::scoped_ptr<T>将对它所存储的指针调用delete。当抛出一个异常时,堆栈是展开的,并且调用scoped_ptr的析构函数。
scoped_ptr<T> 的类模板是不可复制的,它只存储一个类指针,并且不需要T是一个完整的类型(也就是说可以前向声明)。一些编译器再删除一个不完整的类型时并不发出警告,这可能会导致难以检测的错误,但scoped_ptr(以及所有在Boost.SmartPtr中的类)在这些情况下有一个特定的编译时断言。
boost::scoped_ptr<T> 函数等于const std::auto_ptr<T> ,但它也有reset()函数。
另一个例子
boost::shared_ptr的工作原理:
shared_ptr类的内部有一个原子引用计数器。当复制它时,引用计数器递增,而当调用它的析构函数时,引用计数器递减。当引用计数器等于0时,对由share_ptr指向的对象调用delete。
以上代码问题,使用Boost.SmartPtr库,有三种解决方案。它们的区别是data_cpy变量的类型和构造。
工作原理:
这些例子,共享类对引用计数并且当引用计数为零时对一个指针调用delete[]。第三个例子中,为共享指针提供了一个deleter对象,将调用deleter对象来取代对delete的默认调用。
对比:
第一个方法是Boost的传统方法,第二个方法在Boost1.53之前没有实现,但是第二个方法是最快的一个(调用的new较少),第三个方法是可移植性最好的,可以用于旧版本的Boost与C++11 STL的shared_ptr<> (只是要将boost::checked_array_deleter<T> ()改为std::default_delete<T[]> ())。
要修复以上代码,使得process_integers接受函数化对象:
解决方案:使用Boost.Function库,它可以让你存储任何函数、成员函数或函数化对象,如果它的签名与模板参数中所描述的匹配:
boost::function类都有一个默认的构造函数和一个空的状态。
检查一个空的/默认构造状态可以这样做:
fobject_t方法存储在它本身来自函数化对象的数据中,并删除它们的确切类型。使用boost::function对象是安全的,如下面的代码:
尽管Boost.Function库经过多方面的优化,可以存储小的函数化对象,而无须额外的内存分配并且拥有优化的移动赋值运算符,被接受为C++11 STL库的一部分,在头文件<functional> 的std::命名空间中定义,但是,boost::function意味着编译器的优化障碍,这意味着:
将比下面的代码更好地被编译器优化
所以,当不是真的需要它时,应该尽量避免使用Boost.Function,在某些情况下,C++11 auto关键字可以方便地代替它:
一个指向my_ints_function的指针将存放在boost::function类里面,并且对boost::function的调用将被转发到所存储的指针。
依然没有什么需要做的,因为boost::function<> 也可以使用任何难度的lambda函数
以上做法容易出错,可以考虑在容器中存放智能指针:
然而,std::auto_ptr类已经过时,不推荐在容器中使用它,此外,这个例子将无法用C++11编译。
一个很好的解决方案,但是它不能用于C++03,并且仍然要写一个比较器函数化对象。在容器中使用Boost.SmartPtr:
这个解决方案可移植,但仍然要写比较器,而这增加了性能损耗。Boost.PointerContainer库提供了一个良好的并可移植的解决方案:
Boost.PointerContainer库拥有ptr_vector、ptr_array、ptr_set、ptr_multimap类和其他类。所有这些容器都能简化你的工作。在处理指针时,它们将在析构函数中被释放的指针,并简化对指针所指向的数据的访问。
为了在一个成员函数里面捕捉它,需要使用特殊符号this_:
Boost.ScopeExit库不在堆上分配任何额外的内存,并且不使用虚函数。
由于直接基类在非静态数据成员之前进行初始化,无论成员初始化的顺序如何,所以我们不能控制在初始化logger之后再初始化基类。
Boost.Utility库提供了这种情况下的一个快速解决方案,称为boost::base_from_member模板。使用如下:
工作原理
如果直接基类在非静态数据成员之前初始化,并且如果直接基类将按它们出现在基类指定列表的声明顺序初始化,我们需要以某种方式使一个基类成为非静态数据成员,或者制作一个基类,它有一个成员字段带有所需的成员:
base_from_member有一个整数作为第二个模板参数,为了解决我们需要多个相同类型的base_from_member类的问题。base_from_member类既不采用额外的动态内存分配,也没有虚函数。
管理作用域内的类指针
在某些情况下,需要动态分配内存,并在分配的内存中构造一个类,比如:void foo1() { foo_class* p = new foo_class("Some initialization data"); bool something_else_happened = some_function1(p); if(something_else_happened) { delete p; return false; } some_function2(p); delete p ; return true; }
以上代码看似正确,但若some_function1()或some_function2()抛出一个异常,p就不会被删除,当然,也可以通过增加try{}catch{}块来处理,但是代码会难看且难读。
Boost.SmartPtr库中提供boost::scoped_ptr类,有更好的解法:
#include <boost/scoped_ptr.hpp> bool foo3() { boost::scoped_ptr<foo_class> p(new foo_class( "Some initialization data")); bool something_else_happened = some_function1(p.get()); if (something_else_happened) { return false; } some_function2(p.get()); return true; }
boost::scoped_ptr的工作原理:
在析构函数钟,Boost::scoped_ptr<T>将对它所存储的指针调用delete。当抛出一个异常时,堆栈是展开的,并且调用scoped_ptr的析构函数。
scoped_ptr<T> 的类模板是不可复制的,它只存储一个类指针,并且不需要T是一个完整的类型(也就是说可以前向声明)。一些编译器再删除一个不完整的类型时并不发出警告,这可能会导致难以检测的错误,但scoped_ptr(以及所有在Boost.SmartPtr中的类)在这些情况下有一个特定的编译时断言。
boost::scoped_ptr<T> 函数等于const std::auto_ptr<T> ,但它也有reset()函数。
跨方法使用的类指针的引用计数
在不同的执行线程中处理一个包含数据的动态分配的结构,很难去处理释放的问题,在Boost(和C++11)中有一个可以处理这种多线程问题的类,boost::shared_ptr。#include <boost/shared_ptr.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> // 使用thread库,需要链接libboost_thread以及libboost_system库 void process_sp1(const boost::shared_ptr<foo_class>& p); void process_sp2(const boost::shared_ptr<foo_class>& p); void process_sp3(const boost::shared_ptr<foo_class>& p); void foo() { typedef boost::shared_ptr<foo_class> ptr_t; ptr_t p; while (p = ptr_t(get_data())) { boost::thread(boost::bind(&process_sp1, p)).detach(); boost::thread(boost::bind(&process_sp2, p)).detach(); boost::thread(boost::bind(&process_sp3, p)).detach(); } }
另一个例子
#include <string> #include <boost/smart_ptr/make_shared.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> void process_str1(boost::shared_ptr<std::string> p); void process_str2(const boost::shared_ptr<std::string>& p); void foo() { boost::shared_ptr<std::string> ps = boost::make_shared<std::string>( "Guess why make_shared<std::string> " "is faster than shared_ptr<std::string> " "ps(new std::string('this string'))" ); // make_shared()函数要比直接创建shared_ptr对象的方式快且高效, // 因为它内部仅分配一次内存,消除了shared_ptr 构造时的开销。 boost::thread(boost::bind(&process_str1, ps)).detach(); boost::thread(boost::bind(&process_str2, ps)).detach(); }
boost::shared_ptr的工作原理:
shared_ptr类的内部有一个原子引用计数器。当复制它时,引用计数器递增,而当调用它的析构函数时,引用计数器递减。当引用计数器等于0时,对由share_ptr指向的对象调用delete。
管理作用域内的数组指针
前面解决了如何管理资源指针,但是,当处理数组时,需要调用delete[]。而Boost.SmartPointer库不仅有scoped_ptr<> 类,而且有scoped_array<> 类。#include <boost/scoped_array.hpp> #include <boost/thread.hpp> #include <boost/bind.hpp> void may_throw1(const char* buffer); void may_throw2(const char* buffer); void foo() { boost::scoped_array<char> buffer(new char[1024 * 1024 * 10]); may_throw1(buffer.get()); may_throw2(buffer.get()); // 不需要调用delete[] // buffer的析构函数会调用delete[] }
跨方法使用的数组指针的引用计数
#include <cstring> #include <boost/thread.hpp> #include <boost/bind.hpp> void do_process(const char* data, std::size_t size); void do_process_in_background(const char* data, std::size_t size) { // 我们需要复制数据,因为我们不知道它什么时候会被调用者释放 char* data_cpy = new char[size]; std::memcpy(data_cpy, data, size); // 启动执行线程来处理数据 boost::thread(boost::bind(&do_process, data_cpy, size)).detach(); // 不能delete[] data_cpy,因为do_process可能仍然再使用 }
以上代码问题,使用Boost.SmartPtr库,有三种解决方案。它们的区别是data_cpy变量的类型和构造。
// Method 1 #include <cstring> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/shared_array.hpp> void do_process(const char* data, std::size_t size); void do_process1(const boost::shared_array<char>& data, std::size_t size) { do_process(data.get(), size); } void do_process_in_background_v1(const char* data, std::size_t size) { // 我们需要复制数据,因为我们不知道它什么时候会被调用者释放 boost::shared_array<char> data_cpy(new char[size]); std::memcpy(data_cpy.get(), data, size); // 启动执行线程来处理数据 boost::thread(boost::bind(&do_process1, data_cpy, size)).detach(); // 不需要对data_cpy调用delete[], // 当引用计数为零时,data_cpy的析构函数将释放数据 }
// Method 2 #include <cstring> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> void do_process(const char* data, std::size_t size); void do_process_shared_ptr(const boost::shared_ptr<char[]>& data, std::size_t size) { do_process(data.get(), size); } void do_process_in_background_v2(const char* data, std::size_t size) { // 执行速度比第一个方法快 boost::shared_ptr<char[]> data_cpy = boost::make_shared<char[]>(size); std::memcpy(data_cpy.get(), data, size); // 启动执行线程来处理数据 boost::thread(boost::bind(&do_process_shared_ptr, data_cpy, size)).detach(); // 当引用计数为零时,data_cpy的析构函数将释放数据 }
// Method 3 #include <cstring> #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> void do_process(const char* data, std::size_t size); void do_process_shared_ptr2(const boost::shared_ptr<char>& data, std::size_t size) { do_process(data.get(), size); } void do_process_in_background_v3(const char* data, std::size_t size) { // 执行速度比第一个方法快 boost::shared_ptr<char> data_cpy(new char[size], boost::checked_array_deleter<char>()); std::memcpy(data_cpy.get(), data, size); // 启动执行线程来处理数据 boost::thread(boost::bind(&do_process_shared_ptr2, data_cpy, size)).detach(); // 当引用计数为零时,data_cpy的析构函数将释放数据 }
工作原理:
这些例子,共享类对引用计数并且当引用计数为零时对一个指针调用delete[]。第三个例子中,为共享指针提供了一个deleter对象,将调用deleter对象来取代对delete的默认调用。
对比:
第一个方法是Boost的传统方法,第二个方法在Boost1.53之前没有实现,但是第二个方法是最快的一个(调用的new较少),第三个方法是可移植性最好的,可以用于旧版本的Boost与C++11 STL的shared_ptr<> (只是要将boost::checked_array_deleter<T> ()改为std::default_delete<T[]> ())。
在变量中存储任意函数化对象
观察以下代码,我们要传递函数化对象#include <functional> // std::unary_function<>模板所需要 // 为接受int并且不返回任何东西的函数指针制作一个typedef typedef void(*func_t)(int); // 它不能接受函数化对象 void process_integers(func_t f); class int_processor :public std::unary_function<int, void> { const int min; const int max; bool& triggered; public: int_processor(int min,int max,bool& triggered) :min(min),max(max),triggered(triggered) {} void operator()(int i) const { if (i<min || i>max) { triggered = true; } } };
要修复以上代码,使得process_integers接受函数化对象:
解决方案:使用Boost.Function库,它可以让你存储任何函数、成员函数或函数化对象,如果它的签名与模板参数中所描述的匹配:
#include <boost/function.hpp> typedef boost::function<void(int)> fobject_t; // 现在可以接受函数化对象 void process_integers(const fobject_t& f); int main() { bool is_triggered = false; int_processor fo(0, 200, is_triggered); process_integers(fo); std::cout << is_triggered << std::endl; return 0; }
boost::function类都有一个默认的构造函数和一个空的状态。
检查一个空的/默认构造状态可以这样做:
void foo(const fobject_t& f) { // boost::function 是可以转换成bool的 if (f) { // 'f'中有值 // ... } else { // 'f'为空 // ... } }
fobject_t方法存储在它本身来自函数化对象的数据中,并删除它们的确切类型。使用boost::function对象是安全的,如下面的代码:
bool g_is_triggered = false; void set_functional_object(fobject_t& f) { int_processor fo(100,200,g_is_triggered); f = fo; // 'fo'离开作用域并且将被销毁,但'f'即使在作用域外也是可用的 }
尽管Boost.Function库经过多方面的优化,可以存储小的函数化对象,而无须额外的内存分配并且拥有优化的移动赋值运算符,被接受为C++11 STL库的一部分,在头文件<functional> 的std::命名空间中定义,但是,boost::function意味着编译器的优化障碍,这意味着:
std::for_each(v.begin(),v.edn(), boost::bind(std::plus<int>(),10,_1));
将比下面的代码更好地被编译器优化
fobject_t f(boost::bind(std::plus<int>(),10,_1)); std::for_each(v.begin(),v,end(),f);
所以,当不是真的需要它时,应该尽量避免使用Boost.Function,在某些情况下,C++11 auto关键字可以方便地代替它:
auto f = boost::bind(std::plus<int>(),10,_1); std::for_each(v.begin(),v.end(),f);
在变量中传递函数指针
继续前面的例子,现要在process_integers()方法中将一个指针传递给一个函数。void my_ints_function(int i); int main() { // 没什么需要做的,因为boost::function<>也可以从函数指针构造 process_integers(&my_ints_function); }
一个指向my_ints_function的指针将存放在boost::function类里面,并且对boost::function的调用将被转发到所存储的指针。
在变量中传递C++11中的lambda函数
继续前面的例子,现要在process_integers()方法中使用一个lambda函数。依然没有什么需要做的,因为boost::function<> 也可以使用任何难度的lambda函数
// 不带参数什么也不做的lambda函数 process_integers([](int /*i*/){}); // 存储一个引用的lambda函数 std::deque<int> ints; process_integers([&ints](int i){ ints.push_back(i); }); // 修改其内容的lamba函数 std::size_t match_count = 0; process_integers([ints,&match_count](int i) mutable{ if (ints.front() == i) { ++match_count; } ints.pop_front(); });
指针的容器
在容器中存储多态性数据,迫使容器中的数据快速复制,以及对容器内的数据操作有严格的异常需求时,我们需要在容器中存储指针,并使用delete操作符处理它们的析构:#include <set> #include <algorithm> #include <boost/bind.hpp> #include <boost/type_traits/remove_pointer.hpp> #include <cassert> template <class T> struct ptr_cmp :public std::binary_function<T, T, bool> { template <class T1> bool operator()(const T1& v1, const T1& v2)const { return operator()(*v1, *v2); } bool operator()(const T& v1, const T& v2) const { return std::less<T>(*v1, *v2); } }; void example1() { std::set<int*, ptr_cmp<int>> s; s.insert(new int(1)); s.insert(new int(0)); assert(**s.begin() == 0); std::for_each(s.begin(), s.end(), boost::bind(::operator delete, _1)); }
以上做法容易出错,可以考虑在容器中存放智能指针:
// 用于C++03版本 void example2_a() { typedef std::auto_ptr<int> int_aptr_t; std::set<int_aptr_t, ptr_cmp<int>> s; s.insert(int_aptr_t(new int(1))); s.insert(int_aptr_t(new int(0))); assert(**s.begin() == 0); // 资源将被auto_ptr<>释放 }
然而,std::auto_ptr类已经过时,不推荐在容器中使用它,此外,这个例子将无法用C++11编译。
// 用于C++11版本 void example2_b() { typedef std::unique_ptr<int> int_uptr_t; std::set<int_uptr_t, ptr_cmp<int>> s; s.insert(int_uptr_t(new int(1))); s.insert(int_uptr_t(new int(0))); assert(**s.begin() == 0); // 资源将被unique_ptr<>释放 }
一个很好的解决方案,但是它不能用于C++03,并且仍然要写一个比较器函数化对象。在容器中使用Boost.SmartPtr:
#include <boost/shared_ptr.hpp> void example3() { typedef boost::shared_ptr<int> int_sptr_t; std::set<int_sptr_t, ptr_cmp<int>> s; s.insert(int_sptr_t(new int(1))); s.insert(int_sptr_t(new int(0))); assert(**s.begin() == 0); // 资源将被shared_ptr<>释放 }
这个解决方案可移植,但仍然要写比较器,而这增加了性能损耗。Boost.PointerContainer库提供了一个良好的并可移植的解决方案:
#include <boost/ptr_container/ptr_set.hpp> void correct_impl() { boost::ptr_set<int> s; s.insert(new int(1)); s.insert(new int(0)); assert(*s.begin() == 0); // 资源被容器本身释放 }
Boost.PointerContainer库拥有ptr_vector、ptr_array、ptr_set、ptr_multimap类和其他类。所有这些容器都能简化你的工作。在处理指针时,它们将在析构函数中被释放的指针,并简化对指针所指向的数据的访问。
在退出作用域时做一些事
#include <boost/scope_exit.hpp> #include <cstdlib> #include <cstdio> #include <cassert> int main() { std::FILE* f = std::fopen("example_file.txt", "w"); assert(f); BOOST_SCOPE_EXIT(f) { // 无论作用域中发生什么,都将执行这段代码,并且将正确关闭文件 std::fclose(f); }BOOST_SCOPE_EXIT_END // ... system("pause"); return 0; }
为了在一个成员函数里面捕捉它,需要使用特殊符号this_:
class there_more_example { public: void close(std::FILE*); void theres_more_example_func() { std::FILE* f = 0; BOOST_SCOPE_EXIT(f, this_) // 捕获对象"this_" { this_->close(f); }BOOST_SCOPE_EXIT_END } };
Boost.ScopeExit库不在堆上分配任何额外的内存,并且不使用虚函数。
用派生类的成员初始化基类
有这样一个例子,一个有虚函数的基类,它必须用std::ostream的对象的引用进行初始化,而它的派生类,有一个std::ostream的对象并实现了do_process()函数:#include <boost/noncopyable.hpp> #include <sstream> class tasks_processor :boost::noncopyable { std::ostream& log; protected: virtual void do_process() = 0; public: explicit tasks_processor(std::ostream& log_) :log(log_) {} void process() { log << "Starting data processing"; do_process(); } }; class fake_tasks_processor :public tasks_processor { std::ostringstream logger; virtual void do_process() { logger << "Fake processor processed!"; } public: fake_tasks_processor() :tasks_processor(logger) // logger不在这里 , logger() {} };
由于直接基类在非静态数据成员之前进行初始化,无论成员初始化的顺序如何,所以我们不能控制在初始化logger之后再初始化基类。
Boost.Utility库提供了这种情况下的一个快速解决方案,称为boost::base_from_member模板。使用如下:
#include <boost/utility/base_from_member.hpp> // 从boost::base_from_member<T>派生,其中T是必须在基类之前初始化的类型 // 注意顺序,boost::base_from_member<T>必须放在使用T的类前面 class fake_tasks_processor_fixed :boost::base_from_member<std::ostringstream> , public tasks_processor { typedef boost::base_from_member<std::ostringstream> logger_t; // ... public: fake_tasks_processor_fixed() : logger_t() ,tasks_processor(logger_t::member) {} };
工作原理
如果直接基类在非静态数据成员之前初始化,并且如果直接基类将按它们出现在基类指定列表的声明顺序初始化,我们需要以某种方式使一个基类成为非静态数据成员,或者制作一个基类,它有一个成员字段带有所需的成员:
template < typename MemberType, int UniqueID = 0 > class base_from_member { protected: MemberType member; // 构造函数... };
base_from_member有一个整数作为第二个模板参数,为了解决我们需要多个相同类型的base_from_member类的问题。base_from_member类既不采用额外的动态内存分配,也没有虚函数。
相关文章推荐
- iOS深入学习(使用CocoaPods进行第三方资源管理)
- iOS深入学习(使用CocoaPods进行第三方资源管理)
- iOS深入学习(使用CocoaPods进行第三方资源管理)(转)
- 使用CocoaPods进行第三方资源管理(新机配置)
- iOS深入学习(使用CocoaPods进行第三方资源管理)
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(4)-资源管理
- c++11 条款18: 使用std::unique_ptr来进行独享所有权的资源管理
- c++11 条款19:使用std::shared_ptr来进行共享所有权的资源管理
- symfony2如何使用Assetic进行管理资源【原创】
- 使用C++11新特性来实现RAII进行资源管理
- iOS深入学习(使用CocoaPods进行第三方资源管理)
- iOS深入学习(使用CocoaPods进行第三方资源管理)
- 使用Jquery+EasyUI 进行框架项目开发案例讲解之四---组织机构管理源码分享
- 使用对象来管理资源
- servlet学习笔记(3)-使用Java Servlet API 进行会话管理(对session的操作)
- Qt Creator 中使用svn进行版本管理
- ubuntu下,使用svn进行版本管理
- 在SUSE12中使用 Machinery 进行高级系统管理
- 使用EVM进行项目管理时的注意事项