卓航论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 220|回复: 2
打印 上一主题 下一主题

Boost中应用的泛型编程技术

[复制链接]
[至尊红钻5级]发帖数量≥8000篇 [未点亮至尊黄钻]威望不足10点 [未点亮至尊蓝钻]在线时间不足10小时 [未点亮至尊绿钻]贡献度不足10点 [至尊紫钻4级]金币≥20000个 [未点亮至尊粉钻]精华贴数不足10贴 [未点亮至尊黑钻]活跃不足8个
 等级: 
 级别: 论坛元老
 UID:  7   [未点亮普号显示]钻石不足3个
 阁 分: 36567
 阁 望: 0
 阁 献: 0
 活 跃: 0
 发 贴: 12405 (0)
 阁 币: 24162  
性 别: I'm 火星人!
阅读权限: 90
在线时长: 0 小时
注册时间: 2016-10-16
最后登录: 2016-10-18
go
楼主
发表于 2016-10-17 16:25:26 |只看该作者 |倒序浏览
本帖发表于 2016-10-17 16:25:26...阅读 221 人...加油,亲爱的楼主:[db:作者]
1、何谓泛型编程
泛型编程(Generic Programming)关注于产生通用的软 件组件,让这些组件在不同的应用场合都能很容易地重用。在C++中,类模板和函 数模板是进行泛型编程极为有效的机制。有了这两大利器,我们在实现泛型化的 同时,并不需要付出效率的代价。
作为泛型编程的一个简单例子,让我们 看一下在C库中如何让memcpy()函数泛型化。一种实现方法可能是这样的:
void* memcpy(void* region1, const void* region2, size_t n)
{
  const char* first = (const char*)region2;
  const char* last = ((const char*)region2) + n;
  char* result = (char*)region1;
  while (first != last)
    *result++ = *first++;
  return result;
}
这个memcpy()函数已经 在一定程度上进行了泛型化,采取的措施是使用void*,这样该函数就可以拷贝不 同类型的数组。但如果我们想拷贝的数据不是放在数组里,而是由链表来存放呢 ?我们能不能扩展这个概念,让它可以拷贝任意的序列?看看memcpy()的函数体 ,这个函数对传入的序列有一个_最小需求_:它需要用某种形式的指针来遍历这 个序列;访问指针正指向的元素;把元素写到目的地;比较指针以判断何时停止 拷贝。C++标准库把这样的需求进行分组,称之为概念(concepts)。在这个例子 中就有输入迭代器(对应于region1)和输出迭代器(对应于region2)这两个概 念。
如果我们把memcpy()用函数模板重写,使用输入迭代器和输出迭代器 作为模板参数来描述对序列的需求,我们就可以写出一个具有较高重用性的copy ()函数:
template
OutputIterator
copy(InputIterator first, InputIterator last, OutputIterator result)
{
  while (first != last)
    *result++ = *first++;
  return result;
}
使用这个泛型的copy()函数,我们可以拷贝各种各样的序列,只要 它满足了我们指定的需求。对外提供了迭代器的链表,比如std::list,也可以通 过我们的copy()函数来拷贝:
#include
#include
#include [i]
int main()
{
  const int N = 3;
  std::vector[i] region1(N);
  std::list[i] region2;
  region2.push_back (1);
  region2.push_back(0);
  region2.push_back(3);
  std::copy(region2.begin(), region2.end(), region1.begin ());
  for (int i = 0; i
               
2、何谓概念?
概念就是 需求的集合,这些需求可以包含有效表达式、相关类型、不变性,以及复杂度保 证。满足概念中所有需求的类型,称之为此概念的一个样例。一个概念可以是对 别的概念的扩展,称之为概念的细化。
有效表达式:任意的C++表达式。 若某类型是概念的一个样例,那么把该类型的一个对象代入此表达式中,该表达 式必能通过编译。
相关类型:与样例相关的一些类型,它们和样例共同出 现在一个或多个有效表达式中。如果样例类型是自定义类型,则相关类型可以由 类定义中所嵌套的一些typedef来访问。相关类型经常也通过traits类来访问。
不变性:对象的一些运行时特征,必须为真。所有用到这个对象的函数, 都必须保持这种特征。不变性通常以前置条件和后置条件的形式出现。
复 杂度保证:对概念中的有效表达式执行所需时间或资源所做的限制。
C++ 标准库中所使用的概念在SGI STL网站上有详细的文档说明。
3、 Traits
traits类为访问编译时实体(类型、整数常量,或者地址)的相关信息 提供了一条途径。比如,类模板std::iterator_traits看起来是这个样 子:
template
struct iterator_traits {
  typedef ... iterator_category;
   typedef ... value_type;
  typedef ... difference_type;
   typedef ... pointer;
  typedef ... reference;
};
该traits的value_type可以让泛型代码得知该迭代器所“指向”的是 何种类型的数据;而iterator_category对迭代器的能力进行分类,针对不同类型 的迭代器我们选择与之适应的最高效的算法。
traits模板有一个很重要的 特点:它们是非侵入的(non-intrusive)。我们可以为任何类型提供相关信息, 不管它是内建的类型,还是第三方的库中提供的类型。为了给某一特定类型指定 traits,一般采用部分特化或者全特化traits模板的方式。
欲更深入的了 解std::iterator_traits,可以参阅SGI提供的资料。 std::numeric_limits也采用了traits技术,提供表示各内建数值类型 取值范围的常量值。
               
4、标记分派
另外有一种技术经常与traits合用, 那就是标记分派。它依据类型的属性,通过函数重载进行分派。一个很好的例子 就是std::advance()函数。这个函数的功能是将一个迭代器递增n次,对于不同类 型的迭代器可以有各自优化的实现方法。如果是随机访问迭代器(可以以任意的 距离前后跳转),advance()函数可以用i+=n来实现,既简单又高效,只需要常量 时间。而其它的迭代器必须一步一步地递增,需要线性的时间复杂度。如果是双 向迭代器,n就可能为负值,因此必须判断对迭代器到底是增还是减。
标 记分派和traits类的联系很紧密。分派所依据的属性(在这个例子中是 iterator_category)一般都是通过traits类来取得。主advance()函数从 iterator_traits中获得对应于该iterator的iterator_category,然后调用重载 过的advance_dispach()函数。编译器依据作为参数传给advance_dispach()的 iterator_category,选择合适的重载版本。标记只是一个极其简单的类,它的唯 一任务就是为标记分派或者其它类似技术传递必要的信息。
namespace std {
 struct input_iterator_tag { };
 struct bidirectional_iterator_tag { };
 struct random_access_iterator_tag { };
 namespace detail {
   template
  void advance_dispatch(InputIterator& i, Distance n, input_iterator_tag) {
   while (n--) ++i;
  }
  template
  void advance_dispatch(BidirectionalIterator& i, Distance n,
    bidirectional_iterator_tag) {
   if (n >= 0)
     while (n--) ++i;
   else
    while (n++) --i;
   }
  template
  void advance_dispatch(RandomAccessIterator& i, Distance n,
    random_access_iterator_tag) {
   i += n;
  }
 }
 template
 void advance(InputIterator& i, Distance n) {
  typename iterator_traits[I]::iterator_category category;
  detail::advance_dispatch(i, n, category);
 }
}
5、适配器
适配器是一种类模板,建立在其它类型之上,提供新的 接口或者行为。标准库中就使用了适配器,比如std::reverse_iterator通过反转 迭代器的递增/递减行为,适配了迭代器,还有std::stack,通过适配标准容器, 提供一个简单的堆栈接口。
在这里可以找到标准库中所用适配器的深入阐 述。
               
6、类型生成器
类型生成器的工作是依据它的模板参数合成新的类 型[注]。类型生成器产生的新类型一般作为生成器中嵌套的typedef出现。用类型 生成器的主要目的是为了让复杂的类型表达式显得更简单些。比如 boost::filter_iterator_generator:
template
struct filter_iterator_generator {
  typedef iterator_adaptor,
     Value,Reference,Pointer,Category,Distance> type;
};
看起来可真够复杂的。但现在生成一个合适的filter iterator可 就容易多了,只需要写:
boost::filter_iterator_generator::type
就行了。
7、对象生成器
对象生成器是一种函 数模板,依据其参数产生新的对象。可以把它想象成泛型化的构造函数。有些情 况下,欲生成的对象的精确类型很难甚至根本无法表示出来,这时对象生成器可 就管用了。对象生成器的优点还在于它的返回值可以直接作为函数参数,而不像 构造函数那样只有在定义变量时才会调用。Boost和标准库中用到的对象生成器大 多都加了个前缀make_,比如std::make_pair(const T&, const U&)。
看看下面的例子:
struct widget {
 void tweak (int);
};
std::vector widget_ptrs;
通过把两个标准的对象生成器bind2nd和mem_fun合用,我们可以很轻松地tweak所 有的widget:
void tweak_all_widgets1(int arg)
{
  for_each(widget_ptrs.begin(), widget_ptrs.end(),
   bind2nd (std::mem_fun(&widget::tweak), arg));
}
如果不用对象 生成器,上面的函数可能就得这样来实现:
void tweak_all_widgets2(int arg)
{
  for_each(struct_ptrs.begin (), struct_ptrs.end(),
    std::binder2nd >(
     std::mem_fun1_t(&widget::tweak), arg));
}
表达式越复杂,就越需要缩短这些冗长的类型说明,对 象生成器的好处也就越能显现。
8、策略类
策略类就是用来传递行为的模 板参数。标准库中的std::allocator就是策略类,把内存管理的行为应用到标准 容器中。
Andrei Alexandrescu在他的文章中对策略类进入了全面而深入 的分析,他写道:
“策略类精确地反映了设计时的抉择。他们要么 从其它类中继承,要么包含在其它类中。策略类在同样的句法结构上提供了不同 的策略。使用策略的类把它用到的每一个策略都作为模板参数,这样,用户就可 以自由地选择需要使用的策略。
策略类的强大在于它们能够自由地组合在 一起。通过把策略类作为模板参数的办法来组合多种策略,代码量与使用的策略 数只成线性关系。”
Andrei认为策略类的强大源于其小粒度和正交 性。Boost在迭代适配器库中的策略类运用可能淡化了这一卖点。在这个库中,所 有已适配的迭代器的行为都放在一个策略类里面。其实,Boost并不是开先河者。 std::char_traits就是一个策略类,它决定了std::basic_string的行为,尽管它 的名字叫traits而不叫policy。
注:因为C++缺少模板化的typedef,类型 生成器可以作为其替代方案。
分享到: QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏0 支持支持0 反对反对0

使用道具 举报

[至尊红钻3级]发帖数量≥1000篇 [至尊黄钻3级]威望≥500点 [至尊蓝钻1级]在线时间≥10小时 [至尊绿钻9级]贡献度≥80000点 [至尊紫钻9级]金币≥8000000个 [未点亮至尊粉钻]精华贴数不足10贴 [未点亮至尊黑钻]活跃不足8个
 等级: 
 级别: 管理员
 UID:  1   [未点亮普号显示]钻石不足3个
 阁 分: 2147483647
 阁 望: 993
 阁 献: 2147483647
 活 跃: 0
 发 贴: 1532 (0)
 阁 币: 9430326  
性 别: I'm 火星人!
阅读权限: 200
在线时长: 46 小时
注册时间: 2016-10-14
最后登录: 2023-10-3
沙发
发表于 2016-10-18 11:04:42 |只看该作者
本回复帖发表于 2016-10-18 11:04:42,感谢爱情孤儿对本帖的认真回复,你的回复是对楼主莫大的鼓舞
不错不错,楼主您辛苦了。。。

使用道具 举报

[至尊红钻2级]发帖数量≥100篇 [未点亮至尊黄钻]威望不足10点 [未点亮至尊蓝钻]在线时间不足10小时 [未点亮至尊绿钻]贡献度不足10点 [未点亮至尊紫钻]金币不足100个 [未点亮至尊粉钻]精华贴数不足10贴 [未点亮至尊黑钻]活跃不足8个
 等级: 
 级别: 注册会员
 UID:  29   [未点亮普号显示]钻石不足3个
 阁 分: 111
 阁 望: 1
 阁 献: 4
 活 跃: 0
 发 贴: 104 (0)
 阁 币: 1  
性 别: I'm 火星人!
阅读权限: 20
在线时长: 2 小时
注册时间: 2011-1-6
最后登录: 2016-10-20
板凳
发表于 2016-10-19 20:19:11 |只看该作者
本回复帖发表于 2016-10-19 20:19:11,感谢za402对本帖的认真回复,你的回复是对楼主莫大的鼓舞
我抢、我抢、我抢沙发~

使用道具 举报

高级模式
B Color Image Link Quote Code Smilies
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1、请认真发帖,禁止回复纯表情,纯数字等无意义的内容!帖子内容不要太简单!
2、提倡文明上网,净化网络环境!抵制低俗不良违法有害信息。
3、每个贴内连续回复请勿多余3贴,每个版面回复请勿多余10贴!
4、如果你对主帖作者的帖子不屑一顾的话,请勿回帖。谢谢合作!

手机版| 百度搜索:vkee.pw

2012-2015 卓航旗下 GMT+8, 2024-6-16 15:24 , Processed in 2.565453 second(s), 30 queries . Powered by Discuz! X3.2  

禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.如遇版权问题,请及时联系站长(QQ:5213513)

今天是: | 本站已经安全运行: //这个地方可以改颜色

快速回复 返回顶部 返回列表