|
|
April 21 struct integral_c_tag { static const int value = 0; };
template< bool C_ > struct bool_ { static const bool value = C_; typedef integral_c_tag tag; typedef bool_ type; typedef bool value_type; operator bool() const { return this->value; } };
typedef bool_<true> true_; typedef bool_<false> false_;
template< typename T, T N > struct integral_c;
template <class T, T val> struct integral_constant: public integral_c<T, val> { typedef integral_constant<T,val> type; };
template<> struct integral_constant<bool,false>: false_ { typedef integral_constant<bool,false> type; };
template<> struct integral_constant<bool,true>: true_ { typedef integral_constant<bool,true> type; };
template< typename T > struct is_integral : integral_constant<bool,false> {
};
template<> struct is_integral<unsigned char> : integral_constant<bool,true> { /*BOOST_TT_AUX_BOOL_TRAIT_VALUE_DECL(true) \ BOOST_MPL_AUX_LAMBDA_SUPPORT_SPEC(1,is_integral,(unsigned char)) */ };
BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_integral,unsigned char const,true) BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_integral,unsigned char volatile,true) BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_integral,unsigned char const volatile,true) March 04 使用智能指针不会因为忘记delete指针而造成内存泄露。还有当第三方的lib中某些函数返回指针,这样的返回的指针被client使用的时候,lib就会失去对返回的指针的控制,这样delete的指针的任务一般就会交给调用方client,但是如果client忘记调用delete或是调用的时机不正确,都有可能导致问题,在这种情况下最好使用智能指针。 使用智能指针的典型情况: 使用智能指针可以保证异常安全 保证程序在有异常抛出时仍然无内存泄露。 智能指针更好的解决了资源的所有权共享
shared_ptr<boost/shared_ptr.hpp>:使用shared_ptr进行对象的生存期自动管理,使得分享资源所有权变得有效且安全. scoped_ptr<boost/scoped_ptr.hpp>: 用于确保能够正确地删除动态分配的对象。scoped_ptr 有着与std::auto_ptr类似的特性,而最大的区别在于它不能转让所有权而auto_ptr可以。事实上,scoped_ptr永远不能被复制或被赋值!scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权。 weak_ptr<boost/weak_ptr.hpp>:weak_ptr 是 shared_ptr 的观察员。它不会干扰shared_ptr所共享的所有权。当一个被weak_ptr所观察的 shared_ptr 要释放它的资源时,它会把相关的 weak_ptr的指针设为空。使用此辅助指针一般是防止悬空指针。intrusive_ptr<boost/intrusive_ptr.hpp>:shared_ptr的插入是版本,一般不使用,因为需要对使用此指针的类型中增加ref计数功能。但是可以保证不增加指针的大小。 scoped_array<boost/scoped_array.hpp>: scoped_array 为数组做了scoped_ptr为单个对象的指针所做的事情:它负责释放内存。 shared_array<boost/shared_array.hpp>: shared_array 用于共享数组所有权的智能指针。一般指向std::vector的shared_ptr提供了比shared_array更多的灵活性,所以一般使用std::vector<shared_ptr>。
环境boost1_38_0 #include <iostream> #include <string> #include <vector> #include <boost/smart_ptr.hpp> #include <boost/any.hpp> #include <boost/format.hpp>
void PrintIfString(const boost::any& Any) { const boost::shared_ptr<std::string> *s = boost::any_cast<boost::shared_ptr<std::string> >(&Any); if (s && *s) { std::cout << **s << std::endl; }
const boost::weak_ptr<std::string>* s1 = boost::any_cast< boost::weak_ptr<std::string> >(&Any); if(s1) { const boost::shared_ptr<std::string> s = s1->lock(); if(s) { std::cout<<"weak: "<< *s <<std::endl; } }
}
int main(int argc, char* argv[]) { std::vector<boost::any> Stuff; boost::shared_ptr<std::string> SharedString1(new std::string( "Share me.By the way,Boost.any is another useful Boost library")); boost::shared_ptr<std::string> SharedString2(SharedString1); boost::shared_ptr<int> SharedInt1(new int(42)); boost::shared_ptr<int> SharedInt2 (SharedInt1); boost::weak_ptr<std::string> ShareString3(SharedString2); //SharedString1.reset(); 重置智能指针为NULL //SharedString2.reset(); Stuff.push_back(SharedString1); Stuff.push_back(SharedString2); Stuff.push_back(SharedInt1); Stuff.push_back(SharedInt2); Stuff.push_back(ShareString3); for_each(Stuff.begin(), Stuff.end(), PrintIfString); Stuff.clear();
return 0; }
scoped_prt 源码中,支持 if(smartptr) 判断的实现 typedef T * this_type::*unspecified_bool_type; operator unspecified_bool_type() const { return px == 0? 0: &this_type::px; }
令人发晕的类型unspecified_bool_type,这种类型实际上是"指向类的某内部成员变量的指针",不要认为它是指针类型(c必知必会上有说明)。(可以参考<<深入c++对象模型>>来查看这样的偏移值是多少)。unspecified_bool_type可以被当作bool类型使用,它要么为0,要么为1(&this_type::px总是1,错误这个是类内部成员变量的偏移值,cout输出为1是因为转换为bool类型了,可以用printf(”%p”,xx)来打印值。注意vc做了特殊处理,第一个元素偏移值打印为0,不像深入c++对象模型说的是1. 但实际上是转换判断还是为 true 的。
注意这样使用: boost::shared_ptr<int> SharedInt1(new int(42)); 而不是先new 然后赋值智能指针。在函数参数上new可以保证异常安全。除了weak_ptr都可以按这种方法来使用。
在C++标准4.12节中, 对指针与布尔值之间的关系有大致如下的描述: 任何一个指针或成员对象指针都可以转换成布尔值,空指针转换成false, 其它值转换成true. 这样我们就可以利用成员指针来进行转换。通常成员指针不可能(至少不那种容易)被隐式转换(转换的前提毕竟是类型要匹配,或可向上转换)。 对于vs2003.net,如果只安装boost 相对比较简单,可以跳到步骤2,这里要安装boost 的stlport 版本。即同时安装stlport 和 以stlport 为标准库编译 boost . 1、先安装stlport 进入stlport\src 目录 运行nmake –f vc71.mak install 这样默认stlport头文件放入 ....\Microsoft Visual Studio .NET 2003\Vc7\include\stlport 目录 在vc 的工具-〉选项-〉项目-〉vc++目录 中设置stlport头文件目录。 注意目录位置要放在vc 头文件链接的上面,这样#include 标准库是优先选择了stlport 而不是 vc 自带的stl . lib 自动拷贝到了vc7/lib 中可以不用设置。
对于5.02 版本,src目录中没有makefile 文件, 有cmd控制台进入build/ lib 目录,执行完 configure.bat 然后再对vc71.mak 进行 make
2、编译boost, 先编译bjam:
a 运行tools\build\jam_src\build.bat,然后会看见 bin.ntx86目录,里面有bjam.exe
b设置环境变量 如: PATH= e:\boost1.32.0\boost\tools\build\jam_src\bin.ntx86 或者把bjam 拷贝到boost 根目录
SET VC71_ROOT="C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7" 这个也可以不用设置。
使用vc提供的工具 visual studio.net 2003 命令提示控制台,进入boost目录:执行 bjam "-sTOOLS=vc-7_1 " install
便可把相关的头文件和编译后的库文件默认安装到c:\boost。如果不使用stlport 对于vc2003安装就这么简单,
对于支持stlport 的boost 安装执行 bjam "-sTOOLS=vc-7_1-stlport" "-sSTLPORT_PATH=E:\STLPort "-sSTLPORT_VERSION=4.6.2" stage
我安装的stlport是4.6.2版本的,注意上面指定的stlport目录和版本,在编译过程中会出错但能编译完。 如:F:\boost\boost_1_32_0\boost\lexical_cast.hpp(150) : error C2679: 还有其他一些原因不能编译出来的库。 大部分安装都会有这个问题,可以添加 "-sBUILD=<native-wchar_t>off" 选项关闭编译时的/zc:wchar_t选项 bjam "-sTOOLS=vc-7_1-stlport" "-sBUILD=<native-wchar_t>off" "-sSTLPORT_PATH=E:\STLPort" "-sSTLPORT_VERSION=4.6.2" stage 注意按照上面 stlport lib 的目录是在 E:\STLPort\stlport-4.6.2\lib ,后面数字是版本号指定的目录中的数字
编译后在boost 根目录出现一个stage 目录,里面是所有编译过的库。我的stlport 版本有158个文件,vc 200多一些,可见stlport 有些库没出来,多半是跟wchar_t有关的。
完成后可以测试下面的程序 #include "stdafx.h" #include <iostream> #include <boost/regex.hpp> #include <boost/thread.hpp>
int main() { // 3 digits, a word, any character, 2 digits or "N/A", // a space, then the first word again boost::regex reg("\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1"); std::string correct="123Hello N/A Hello"; std::string incorrect="123Hello 12 hello"; assert(boost::regex_match(correct,reg)==true); assert(boost::regex_match(incorrect,reg)==false); boost::regex reg1("(new)|(delete)"); boost::smatch m;std::string s= "Calls to new must be followed by delete. \ Calling simply new results in a leak!"; if (boost::regex_search(s,m,reg1)) { // Did new match? if (m[1].matched) std::cout << "The expression (new) matched!\n"; if (m[2].matched) std::cout << "The expression (delete) matched!\n"; } return 0; }
注意编译debug 版本设置工程-〉c++ -> 命令行附加选项加入 /GX /D_STLP_DEBUG。代码生成-〉运行时库必须选择多线程版本的, 没有 /D_STLP_DEBUG 编译时回停留在这里 elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-gdp" # pragma message("warning: STLPort debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # else # define BOOST_LIB_RT_OPT "-p"
如果没有指定使用多线程版本,编译会提示你缺少libboost_regex-vc71-sgdp-1_xx_1.lib 的库,其实默认编译没有这个库。可能指定一些编译参数会有吧. 如果不指定多线程的话。stlport 的库也有链接问题。这些库基本都是多线程版本的(不代表线程安全)。最好指定为多线程动态库 /md 或 /mdd 版本。静态库编译出来太大了
对于test 库,程序能编译但不能链接,对于vc自带stl 与stl port 都一样,不知道哪里需要设置。里面有些文件直接在vc工程中都不能编译,可能版本还不是太好,错误如下 test_tools::tt_detail::check_impl , 搜搜网上存在这样问题的不少。这样BOOST_CHECK 之类的宏就不能使用了 error LNK2019: 无法解析的外部符号 "void __cdecl boost::test_tools::tt_detail::check_impl(class boost::test_tools::predicate_result const &,class boost::basic_wrap_stringstream<char> &,class boost::unit_test::basic_cstring<char const >,unsigned int,enum boost::test_tools::tt_detail::tool_level,enum boost::test_tools::tt_detail::check_type,unsigned int,...)" (?check_impl@tt_detail@test_tools@boost@@YAXABVpredicate_result@23@AAV?$basic_wrap_stringstream @D@3@V?$basic_cstring@$$CBD@unit_test@3@IW4tool_level@123@W4check_type@123@IZZ)
1.下载boost source 到http://www.boost.org下载最新版本的boost,我目前下载的是1.34.1,将之解压到c:\boost_1_34_1\
2.编译bjam C:\boost_1_34_1\tools\jam\src下,执行build.bat,然后会在C:\boost_1_34_1\tools\jam\src\bin.ntx86\生成一个bjam.exe,将bjam.exe复制到c:\boost_1_34_1\下。 我这不能编译。也可以下载一个
3.设定编译环境
修改user-config.jam (C:\boost_1_34_1\tools\build\v2\user-config.jam) 的MSVC configuration
A:vs2003.net
using msvc : 7.1 ;
B:vs2005.net
using msvc : 8.0 : : <compileflags>/wd4819 <compileflags>/D_CRT_SECURE_NO_DEPRECATE <compileflags>/D_SCL_SECURE_NO_DEPRECATE <compileflags>/D_SECURE_SCL=0 ;
在VC8.0出现的warning,主要是以下2类
a.C4819 : 代码中cp950无法显示的字元.
b.VC8.0特有的的safe_code技术.
宏解释: _CRT_SECURE_NO_DEPRECATE和_SCL_SECURE_NO_DEPRECATE用于关闭safe code代码警告, _SECURE_SCL用于控制是否用safe code对STL边界进行检查。
C: 加上 python , 目前我用的是2.5版
using python : 2.5 ;

4:下载bzip2-1.0.4 zlib-1.2.3, icu4c-3.6:
bzip2-1.0.4 下载:http://www.bzip.org/
zlib-1.2.3 下载: http://www.zlib.net/
icu4c-3.6 下载: http://www.icu-project.org/
5:下载python2.5 , 安装到c:\
python2.5 下载: http://www.python.org/
6:写一个批处理文件,内容是:
SET BZIP2_SOURCE="D:/bzip2-1.0.4"
SET ZLIB_SOURCE="D:/zlib-1.2.3"
SET ICU_PATH="D:/icu4c-3.6"
bjam --toolset=msvc-8.0 --stagedir=./lib_x86 --builddir=./ address-model=32 link=static runtime-link=static threading=multi stage debug release
bjam --toolset=msvc-8.0 --stagedir=./lib_x64 --builddir=./ address-model=64 link=static runtime-link=static threading=multi stage debug release
或
SET BZIP2_SOURCE="D:/bzip2-1.0.4"
SET ZLIB_SOURCE="D:/zlib-1.2.3"
SET ICU_PATH="D:/icu4c-3.6"
bjam --toolset=msvc-8.0 --stagedir=./lib_x86 --builddir=./ address-model=32 link=shared runtime-link=shared threading=multi stage debug release
bjam --toolset=msvc-8.0 --stagedir=./lib_x64 --builddir=./ address-model=64 link=shared runtime-link=shared threading=multi stage debug release
7.将批处理文件放到C:\boost_1_34_1, 执行批处理文件
boost_1_38_0 编辑 bjam
必须先在环境变量path中指定要编译的bjam目录,不然不能使用build_xx.bat进行编译,并提示一个诡异的错误。

Boost Graph Library 快速入门 图领域的数据结构和算法在某些方面比容器更为复杂,图算法在图中移动有着众多的路线,而STL使用的抽象迭代器接口不能有效的支持这些。作为替换,我们为图提供了一个的抽象的结构,其与容器迭代器的目的类似(尽管迭代器扮演着更大的角色)。图1 描述了STL 和BGL 之间的对比 图1: The analogy between the STL and the BGL.
图由一系列顶点vertices,以及连接顶点的边edges组成. 如图2描述了一个拥有5个顶点和11条边的有向图directed graph. 离开一个顶的边称为该点的out-edges。边 {(0,1),(0,2),(0,3),(0,4)} 都是节点0的out-edges ,进入一个顶点的边称为该点的in-edges , 边{(0,4),(2,4),(3,4)} 是节点0的in-edges
图2 一个有向图例子
在后面的章节中,我们使用BGL构造上图并展示各种操作。全部的代码可以在examples/quick_tour.cpp 中找到,下面每个章节都是这个例子文件的一个片断。
构造一个图
在这个例子中,我们将使用BGL 邻接表adjacency_list 类来示范BGL接口中的主要概念.adjacency_list类提供了典型邻接表数据结构的一个泛型版本. adjacency_list 是一个拥有6个模板参数的模板类。但我们只使用了前3个参数,剩余的3个使用默认参数。头两个模板参数(vecS, vecS)分别用来描述离开顶点的out-edges边和图中顶点的集合所使用的数据结构,(阅读 Choosing the Edgelist and VertexList 章节可以获得更多关于平衡不同数据结构的信息) 第三个参数, 使用bidirectionalS表示选择一个可访问出、入边的有向图,directedS 为选择一个仅提供出边的有向图。undirectedS 表示选择一个无向图
一旦我们选定了图的类型,我们可以创建一个图2所示的图。声明一个图对象,使用 MutableGraph 接口中的add_edge() 函数来填充边,在这个例子中我们简单的使用 pairs 数组edge_array来建立边在这个例子中我们简单的使用 pairs 数组edge_array来建立边
#include <iostream> // for std::cout #include <utility> // for std::pair #include <algorithm> // for std::for_each #include <boost/graph/graph_traits.hpp> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp> using namespace boost;
int main(int,char*[]) { typedef adjacency_list<vecS, vecS, bidirectionalS> Graph; // Make convenient labels for the vertices enum { A, B, C, D, E, N }; //代表 0 ,1,2,3,4 顶点 const int num_vertices = N; const char* name = "ABCDE"; //图中的边 typedef std::pair<int, int> Edge; Edge edge_array[] = { Edge(A,B), Edge(A,D), Edge(C,A), Edge(D,C), Edge(C,E), Edge(B,D), Edge(D,E) }; const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]); // 创建一个拥有5个顶点的图对象 Graph g(num_vertices); // 给图对象添加边 for (int i = 0; i < num_edges; ++i) add_edge(edge_array[i].first, edge_array[i].second, g); return 0; } 我们可以使用图的edge iterator constructor 构造函数来代替为每个边调用add_edge()函数,这种方法 更具代表性比add_edge()更有效率, edge_array 指针可以被视为迭代器,所以我们可以传递数组开始和结束的指针给图构造函数 Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices); 同样可以使用 MutableGraph 接口的add_vertex()和remove_vertex() 来为图添加和删除顶点, 而不是一开始就创建一个拥有一定数目顶点的图
访问顶点集合 现在我们创建了一个图,我们可以使用图接口访问图数据,首先我们可以通过 VertexListGraph 接口的vertices() 函数来访问图中所有的顶点。这个函数返回一个顶点迭代器的std::pair 类型(第一个迭代器指向顶点的开始,第二个迭代器指向顶点的结束)。提领一个顶点迭代器放回一个顶点对象。顶点迭代器的类型可由graph_traits 类取得,值得注意的是不同的图类型可能有不同的顶点迭代器类型,这也是为什么我们需要graph_traits 类的原因。给定一个图类型,graph_traits类能提供该图的vertex_iterator类型,下面的例子打印了图中每个顶点的索引。所有的顶点和边属性,以及索引,可以通过property map 对象访问。property_map 类可用来获得指定属性(通过指定BGL预定义的vertex_index_t来取得索引)的property map 类型,通过调用函数get(vertex_index, g) 来获得图当前的property map对象 int main(int,char*[]) { //获得顶点索引的 property map typedef property_map<Graph, vertex_index_t>::type IndexMap; IndexMap index = get(vertex_index, g);
std::cout << "vertices(g) = "; typedef graph_traits<Graph>::vertex_iterator vertex_iter; std::pair<vertex_iter, vertex_iter> vp; for (vp = vertices(g); vp.first != vp.second; ++vp.first) std::cout << index[*vp.first] << " "; std::cout << std::endl; return 0; } 输出结果: vertices(g) = 0 1 2 3 4
访问边集合 一个图的边集合可以使用EdgeListGraph接口中的 edges()函数访问。与vertices() 函数类似,这个函数也返回一对迭代器,但在这里的迭代器是边迭代器edge iterators。提领边迭代器可以获得一个边对象,调用source()和target()函数可以取得边连接的两个顶点。这次我们使用tie()辅助函数,而不是为迭代器声明一个pair类型,这个便利的函数可以用来分开std::pair 到两个分离的变量,这里是ei 和 ei_end,这样比创建一个std::pair 类型方便。这也是我们为BGL选择的方法
int main(int,char*[]) { std::cout << "edges(g) = "; graph_traits<Graph>::edge_iterator ei, ei_end; for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) std::cout << "(" << index[source(*ei, g)]<< "," << index[target(*ei, g)] << ") "; std::cout << std::endl; return 0; } 输出结果: edges(g) = (0,1) (0,2) (0,3) (0,4) (2,0) (2,4) (3,0)(3,1) (3,4) (4,0) (4,1)
邻接结构
在下面的例子中我们通过观察一个特殊的顶点来展示图的邻接结构,我们将看到顶点的 in-edges, out-edges, 以及他的邻接点adjacent vertices. 我们将这些封装到一个"exercise vertex" 函数对象,并针对图的每个顶点调用它。为了示范BGL同STL协作的能力, 我们使用STL的for_each() 函数迭代每个顶点并调用此函数. int main(int,char*[]) { std::for_each(vertices(g).first, vertices(g).second, exercise_vertex<Graph>(g)); return 0; } 当我们访问每个顶点的信息时需要使用到图对象,所以我们把exercise_vertex写成一个函数对象而不是函数,在std::for_each()执行期间,使用函数对象可以给我们提供了一个位置来保持对图对象的引用。为了能够处理不同的图对象,我们将此函数对象模板化。这里是exercise_vertex 函数对象的开始 template <class Graph> struct exercise_vertex { exercise_vertex(Graph& g_) : g(g_) {} Graph& g; };
顶点描述符
在撰写函数对象operator()方法时,我们首先要知道的是图中顶点对象的类型。顶点类型用来声明operator()中的参数。确切的说,我们实际上并不处理顶点对象,而是使用顶点描述符vertex descriptors. 许多图结构(如邻接表adjacency lists)并不需要存储顶点对象,而另一些存储(例如 pointer-linked graphs),这些不同将被顶点描述符对象的黑箱操作所隐藏。顶点描述符由图类型提供,在后面章节将介绍通过对操作符调用函数out_edges(), in_edges(), adjacent_vertices(),和property map来访问图信息。顶点描述符类型可以通过graph_traits类获得,下面语句中的typename 关键字是必须的,因为在范围操作符::左边(graph_traits<Graph>类型)由模板参数(Graph类型)确定。下面是我们定义的函数对象 template <class Graph> struct exercise_vertex { typedef typename graph_traits<Graph>::vertex_descriptor Vertex; void operator()(const Vertex& v) const { } };
Out-Edges, In-Edges, 和Edge 描述符
可以通过IncidenceGraph接口中的out_edges()函数来访问一个顶点的out-edges, 这个函数需要两个参数:第一个参数是顶点,第二个是图对象。函数返回一对迭代器,来提供对一个顶点所有out-edges的访问(与vertices()函数返回pair对象类似)。这些迭代器称为out-edge iterators, 提领这些迭代器将返回一个边描述符对象,边描述符跟顶点描述符扮演类似性质的角色,也是图类型提供的黑盒,后面的代码片断按source-target顺序打印了顶点v对应的每个out-edge边上的两个点 template <class Graph> struct exercise_vertex { void operator()(const Vertex& v) const { //...... typedef graph_traits<Graph> GraphTraits; typename property_map<Graph, vertex_index_t>::type index = get(vertex_index, g);
std::cout << "out-edges: "; typename GraphTraits::out_edge_iterator out_i, out_end; typename GraphTraits::edge_descriptor e; for (tie(out_i, out_end) = out_edges(v, g);out_i != out_end; ++out_i) { e = *out_i; Vertex src = source(e, g), targ = target(e, g); std::cout << "(" << index[src] << "," << index[targ] << ") "; } std::cout << std::endl; } }; 对于顶点0 输出结果是: out-edges: (0,1) (0,2) (0,3) (0,4)
in_edges() 函数位于BidirectionalGraph接口中,此函数可以通过in-edge迭代器访问一个顶点所有的in-edges。 只有当邻接表的Directed(第三个)模板参数设为bidirectionalS 才能使用此函数. 而指定bidirectionalS代替directedS时将会花费更多的空间 template <class Graph> struct exercise_vertex { void operator()(const Vertex& v) const { //....... 省略与上面重复代码 std::cout << "in-edges: "; typedef typename graph_traits<Graph> GraphTraits; typename GraphTraits::in_edge_iterator in_i, in_end; for (tie(in_i, in_end) = in_edges(v,g); in_i != in_end; ++in_i) { e = *in_i; Vertex src = source(e, g), targ = target(e, g); std::cout << "(" << index[src] << "," << index[targ] << ") "; } std::cout << std::endl; } }; 对于顶点 0 输出是: in-edges: (2,0) (3,0) (4,0)
邻接点
当给出一个顶点的所有的out-edges边时,这些边上的目标点对于源点邻接。有时一个算法不需要关注一个图的边,而是仅关心顶点。因此图形接口AdjacencyGraph 提供了adjacent_vertices()函数来直接访问邻接点。此函数返回一对adjacency iterators ,提领一个邻接点迭代器将会得到领接顶点的顶点描述符。 template <class Graph> struct exercise_vertex { void operator()(Vertex v) const { //....... std::cout << "adjacent vertices: "; typename graph_traits<Graph>::adjacency_iterator ai; typename graph_traits<Graph>::adjacency_iterator ai_end; for (tie(ai, ai_end) = adjacent_vertices(v, g);ai != ai_end; ++ai) std::cout << index[*ai] << " "; std::cout << std::endl; } };
给你的图添加一些颜色
BGL实现尽可能灵活地适应图的附加属性,举个例子,属性如边的权重存在于在图对象的整个生命周期都,因此让图对象管理这个属性的存储将会带来很多便利;另外,属性如顶点颜色只在某个算法的运行期内需要,将此属性和图对象分开存储将会更好。第一种属性称为内在存储属性,而第二种称为外在存储属性。BGL 在图算法中为两种属性提供了一致的访问接口property map,此接口在章节Property Map Concepts中有详细描述。另外,PropertyGraph 配接器也为获得一个内在存储属性的property map 对象定义了接口
BGL 邻接表类允许用户通过设置图对象模版参数来指定内在存储属性,如何实现这些在Internal Properties 章节有详细论述。外在存储属性有多种创建方法,尽管他们基本上作为分离参数传递给图算法。一个简单存储外在属性的办法是通过顶点或边的索引来创建一个索引数组。如邻接表中的VertexList模版参数指定为vecS,每个顶点的索引将会自动建立。通过指定vertex_index_t作为模版参数的property map对象来访问这些索引。每个边虽不能自动建立索引。但是可以通过使用属性机制把索引和边联系起来,来索引其他的外在存储属性。
在下面的例子中,我们创建一个图并执行dijkstra_shortest_paths()算法,完整的源代码在例子examples/dijkstra-example.cpp中。Dijkstra 算法用来计算从起始顶点到其他顶点的最短路径。Dijkstra 算法要求设置每个边的权重和每个顶点的距离,这里我们把权重做为一个内在属性,距离作为外在属性。对于权重属性,我们创建属性类并指定int 作为权重类型,edge_weight_t 作为属性标记(一个BGL预定义的属性标记)。此权重属性类将作为邻接表adjacency_list 的一个模版参数
选择listS或者vecS类型取决于我要在邻接表中使用的数据结构(可以看 Choosing the Edgelist and VertexList章节)。directedS 类型指定图为有向图(相对的是无向图)。后面的代码展示了一个图类型的声明和初始化,以及带权重属性的边如何传递给(使用迭代器作为参数的)图构造函数(要求随机迭代器) typedef adjacency_list<listS, vecS, directedS, no_property, property<edge_weight_t, int> > Graph; typedef graph_traits<Graph>::vertex_descriptor Vertex; typedef std::pair<int,int> E;
const int num_nodes = 5; E edges[] = { E(0,2), E(1,1), E(1,3), E(1,4), E(2,1), E(2,3), E(3,4), E(4,0), E(4,1) }; int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1}; Graph G(edges + sizeof(edges) / sizeof(E), weights, num_nodes);
对于外部距离属性,我们使用std::vector 来存储, BGL 算法视随机迭代器为property maps。所以我们能够传递距离数组vector迭代器到Dijkstra's 算法。紧接上面的例子,下面的代码创建了一个distance vector, 然后调用Dijkstra's 算法(内部使用了权重属性),输出结果: // vector for storing distance property std::vector<int> d(num_vertices(G)); // get the first vertex Vertex s = *(vertices(G).first); // invoke variant 2 of Dijkstra's algorithm dijkstra_shortest_paths(G, s, distance_map(&d[0]));
std::cout << "distances from start vertex:" << std::endl; graph_traits<Graph>::vertex_iterator vi; for(vi = vertices(G).first; vi != vertices(G).second; ++vi) std::cout << "distance(" << index(*vi) << ") = " << d[*vi] << std::endl; std::cout << std::endl;
结果是: distances from start vertex: distance(0) = 0 distance(1) = 6 distance(2) = 1 distance(3) = 4 distance(4) = 5
使用Visitors扩充图算法
通常一个库中的算法能够满足你大部分的需求,但事无绝对,例如在前面的章节中,我们使用Dijkstra's 算法来计算到每个顶点的最短路径,但可能我们想记录路径最短的树,可以通过在记录最短路径树中记录每个节点的前驱来实现。 当然我们最好能够避免重写Dijkstra's 算法,并且只增加记录前辈节点的额外需求[1],在STL中,可以使用仿函数作为算法的可选参数来提供这种伸缩性。在BGL中,visitors 扮演着类似的角色。Visitor 类似stl仿函数。仿函数只有一个执行函数,但Visitor 拥有更多的方法,每个方法将在明确定义的算法点被调用。Visitor 函数在Visitor Concepts章节有详细说明。BGL 为通常的任务提供了几种visitor,包括记录前驱节点的visitor.作为扩充BGL的一种方法鼓励用户写自己的visitor.这里我们将迅速浏览实现和使用前驱记录 , 由于我们使用dijkstra_shortest_paths()算法,所以我们创建的visitor也必须是一个Dijkstra Visitor.
record_predecessors visitor 的泛函性分成两部分。我们使用一个property map来存储和访问前驱属性。前驱 visitor 只负责记录前驱节点。为了实现这些,我们创建一个使用模版参数的record_predecessors类。由于这个visitor将在一个visitor方法中被填充,我们从一个提供空方法的dijkstra_visitor类继承。predecessor_recorder 类的构造函数将接受一个property map 对象,并把他保存在数据成员中。 template <class PredecessorMap> class record_predecessors : public dijkstra_visitor<> { public: record_predecessors(PredecessorMap p) : m_predecessor(p) { }
template <class Edge, class Graph> void edge_relaxed(Edge e, Graph& g) { // set the parent of the target(e) to source(e) put(m_predecessor, target(e, g), source(e, g)); } protected: PredecessorMap m_predecessor; }; 记录前驱节点的工作十分简单,当Dijkstra's algorithm算法释放一个边的时候(添加他到最短路径树中) 我们记录源顶点作为目标顶点的前驱。稍后,如果边再次释放前驱属性将被新的前驱重写,这里我们使用put() 函数在property map中记录前驱。Visitor的edge_filter将告诉算法什么时候调用explore()方法。我们希望边在最短路径树中被通知,所以我们指定tree_edge_tag标记
最后,我们创建一个辅助函数来更方便的创建predecessor visitors,所有的BGL visitor 都有一个类似的辅助函数。 template <class PredecessorMap> record_predecessors<PredecessorMap> make_predecessor_recorder(PredecessorMap p) { return record_predecessors<PredecessorMap>(p); } 现在我们准备在Dijkstra's 算法中使用record_predecessors。BGL 的Dijkstra's 算法配备了一个vistitors句柄,所以我们只需要传入我们新的visitor即可。 在这个例子中我们只需要使用1个visitor,尽管BGL在算法中配置了多visitors句柄参数??(参见Visitor Concepts章). using std::vector; using std::cout; using std::endl; vector<Vertex> p(num_vertices(G)); //the predecessor 数组 dijkstra_shortest_paths(G, s, distance_map(&d[0]). visitor(make_predecessor_recorder(&p[0])));
cout << "parents in the tree of shortest paths:" << endl; for(vi = vertices(G).first; vi != vertices(G).second; ++vi) { cout << "parent(" << *vi; if (p[*vi] == Vertex()) cout << ") = no parent" << endl; else cout << ") = " << p[*vi] << endl; } 输出结果: parents in the tree of shortest paths: parent(0) = no parent parent(1) = 4 parent(2) = 0 parent(3) = 2 parent(4) = 3
注意: 新版本Dijkstra's algorithm包括了一个用来记录前驱的指定参数,所以前驱visitor 并不需要。但上面仍不失为一个好的例子。
September 25 简介: Pool分配是一种分配内存方法,用于快速分配同样大小的内存块, 尤其是反复分配/释放同样大小的内存块的情况。 使用: 1. pool 快速分配小块内存,如果pool无法提供小块内存给用户,返回0。 Example: void func() { boost::pool<> p(sizeof(int)); ^^^^^^^^^^^ 指定每次分配的块的大小 for (int i = 0; i < 10000; ++i) { int * const t = p.malloc(); pool分配指定大小的内存块;需要的时候,pool会向系统 申请大块内存。 ... // Do something with t; don't take the time to free() it p.free( t ); 释放内存块,交还给pool,不是返回给系统。 } pool的析构函数会释放所有从系统申请到的内存。 } 2. object_pool 与pool的区别在于:pool需要指定每次分配的块的大小,object_pool需要指定 每次分配的对象的类型。 Example: struct X { ... }; // has destructor with side-effects void func() { boost::object_pool<X> p; ^ for (int i = 0; i < 10000; ++i) { X * const t = p.malloc(); 注意;X的构造函数不会被调用,仅仅是分配大小为sizeof(X) 的内存块。如果需要调用构造函数(像new一样),应该调用 construct。比如: X * const t = p.construct(); ... } } 3. singleton_pool 与pool用法一样。不同的是:可以定义多个pool类型的object,都是分配同样 大小的内存块;singleton_pool提供静态成员方法分配内存,不用定义object。 Example: struct MyPoolTag { }; typedef boost::singleton_pool<MyPoolTag, sizeof(int)> my_pool; void func() { for (int i = 0; i < 10000; ++i) { int * const t = my_pool::malloc(); ^^^^^^^^^ 和pool不一样。 ... }
my_pool::purge_memory(); 释放my_pool申请的内存。 } 4. pool_alloc 基于singleton_pool实现,提供allocator(用于STL等)。
Example: void func() { std::vector<int, boost::pool_allocator<int> > v; for (int i = 0; i < 10000; ++i) v.push_back(13); } 需要的话,必须自己显式地调用 boost::singleton_pool<boost::pool_allocator_tag, sizeof(int)>:: release_memory() 把allocator分配的内存返回系统。 实现: pool每次向系统申请一大块内存,然后分成同样大小的多个小块, 形成链表连接起来。每次分配的时候,从链表中取出头上一块,提 供给用户。链表为空的时候,pool继续向系统申请大块内存。 一个小问题:在pool的实现中,在申请到大块内存后,马上把它分 成小块形成链表。这个过程开销比较大。即你需要分配一小块内存 时,却需要生成一个大的链表。用如下代码测试, boost::pool<> mem_pool(16);
for(i = 0; i < NPASS; i++) { period = clock(); for(n = 0; n < NITEM; n++) { array_ptr[n] = (int *)mem_pool.malloc(); } for(n = 0; n < NITEM; n++) { mem_pool.free(array_ptr[n]); } period = clock() - period; printf("pool<> : period = %5d ms\n", period); }
可以发现,第一遍花的时间明显多于后面的。 而且在pool的使用过程中如果不是恰好把链表中所有的小块都用上 的话,在链表中最后的一些小块会始终用不上。把这些小块加入链 表是多余的。虽然这个开销可能很小:)
|