第一章 STL概论与版本

1.1 STL概论

为了建立数据结构和算法的一套标准,并且降低其间的耦合(coupling)关系以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability), 提高复用性,所以C++诞生了STL。

STL的价值在于两方面:

  1. 低层次:STL带给我们一套极具实用价值的零部件,以及一个整合的组织。
  2. 高层次:以泛型思维(Generic Paradigm)为基础的、 系统化的、条理分明的“软件组件分类学(components taxonomy) "。

STL是一个抽象概念库(library of abstract concepts) , 这些“抽象概念“ 包括最基础的:

  1. Assignable(可被赋值)
  2. Default Constructible (不需任何参数就可构造)
  3. Equality Comparable (可判断是否等同)
  4. LessThan Comparable (可比较大小)
  5. Regular(正规)

高阶一点的概念则包括:

  1. Input Iterator (具输入功能 的迭代器)
  2. Output Iterator (具输出功能的迭代器)
  3. Forward Iterator (单向迭代 器)
  4. Bidirectional Iterator (双向迭代器)
  5. Random Access Iterator (随机存取迭 代器)
  6. Unary Function (一元函数)
  7. Binary Function (二元函数)
  8. Predicate(传回真假值的一元判断式)
  9. BinaryPredicate (传问真假值的二元判断式)

更高阶的概念包括:

  1. sequence container (序列式容器)
  2. associative container (关联式容器)

STL的创新价值便在于具体叙述了上述这些抽象概念,并加以系统化。

1.2 STL六大组件 功能与运用

STL提供六大组件,彼此可以组合套用:

  1. 容器(containers): 各种数据结构,如vector,list, deque, set, map,用来存放数据,从实现的角度来看,STL容器是一种class template。就体积而言,这一部分很像冰山在海面下的比率。
  2. 算法(algorithms): 各种常用算法如sort,search, copy, erase··· 从实现的角度来看,STL算法是一种function template。
  3. 迭代器(iterators): 扮演容器与算法之间的胶合剂,是所谓的泛型指针, 共有五种类型,以及其它衍生变化。从实现的角度来看,迭代器是一 种将operator*, operator->, operator++, operator-- 等指针 相关操作予以重载的class template。 所有STL容器都附带有自己专属的迭代器, 只有容器设计者才知道如何遍历自己的元素。 原生指针(native pointer)也是 一种迭代器。
  4. 仿函数(functors): 行为类似函数,可作为算法的某种策略(policy)。从实现的角度来看,仿函数是一种重载了operator()的class或class template。一般函数指针可视为狭义的仿函数。
  5. 配接器(adapters) : 一种用来修饰容器 (containers)或仿函数(functors) 或迭代器(iterators)接口的东西, 例如,STL提供的queue和 stack, 虽然看似容器,其实只能算是一种容器配接器,因为它们的底部完全借助deque, 所有操作都由底层的deque供应。 改变functor接口者, 称为function adapter; 改变container接口者,称为container adapter; 改变iterator 接口者, 称为 iterator adapter。 配接器的实现技术很难一言以蔽之, 必须逐 一分析。
  6. 配置器(allocators) : 负责空间配置与管理, 从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放class template。

STL六大组件的交互关系:
在这里插入图片描述

Container通过Allocator取得数据储存空间,Algorithm通过Iterator存取Container内容,Functor可以协助Algorithm 完成不同的策略变化,Adapter可以修饰或套接Functor。

1.3 GUN源代码开放精神

  • 全世界所有的STL实现版本,每一个头文件都有一份声明,允许任何人任意运用、拷贝、修改、传播、贩卖这些代码, 无需付费,唯一的条件是必须将该份声明置于使用者新开发的文件内。这种开放源代码的精神,一般统称为open source。
    • GNU这个名称是计算机族的幽默展现,代表GNU is Not Unix。当时Unix是 计算机界的主流操作系统。

      • GNU以所谓的GPL(General Public License, 广泛开放授权)来保护(或说控制)其成员:使用者可以自由阅读与修改GPL软件的源代码,但如果使用者要 传播借助GPL软件而完成的软件,他们必须也同意GPL规范。GPL对于版权(copyright)观念带来巨大的挑战,甚至被称为“反版权”。
      • 本书所采用的GCC套件是CygnusC++Z.91 for Windows,又称为EGCSU 1.1。

1.4 HP实现版本

HP版本是所有STL实现版本的始祖。每一个HP STL头文件都有如下一份声明,允许任何人免费使用、拷贝、修改、传播、贩卖这份软件及其说明文件,唯一需要遵守的是,必须在所有文件中加上HP的版本声和运用权限声明,这种授权并不属于GNU GPL范畴,但属于 open source范畴.
在这里插入图片描述

1.5 P.J.Plauger实现版本

P.J. Plauger版本由P.J.Plauger开发,本书后继章节皆以PJSTL称呼这一版 本。PJ版本承继HP版本,所以它的每一个头文件都有HP的版本声明,P .J. Plauger版本被VisualC++采用,此外还加上P.J.Plauger的个人版权声明:
在这里插入图片描述
这个产品既不属于open source范畴,更不是GNUGPL。这么做是合法的,因为HP的版权声明并非GPL,并没有强迫其衍生产品必须开放源代码。

1.6 Rouge Wave 实现版本

Rouge Wave版本由RougeWave公司开发,本书后继章节皆以RWSTL称呼,这一版本,RW版本继承HP版本,所以它的每一个头文件都有HP的版本声明,Rouge Wave版本被C++Builder采用,此外还加上Rouge Wave的公司版权声明:
在这里插入图片描述
这份产品既不属于opensource范畴,更不是GNUGPL。这么做是合法的,因为HP的版权声明并非GPL,并没有强迫其衍生产品必须开放源代码。

1.7 STLport实现版本

网络上有个STLport站点,提供个以SGI STL为蓝本的高度可移植性实现版本。本书附录C列有孟岩先生所写的一篇文章,介绍STLport移植到VisualC++和C++Builder的经验。SGISTL (下节介绍)属于开放源代码组织的一员,所以STLport有权利那么做。

1.8 SGI STL实现版本

SGI版本由Silicon Graphics Computer Systems, Inc. 公司发展,承继HP版本。所以它的每一个头文件也都有HP的版本声明。 此外还加上SGI的公司版权声明。 从其声明可知,它属千open source的一 员,但不属于GNUGPL (广泛开放授权),SGI版本被GCC采用。

在这里插入图片描述
1.8.1 GNU C++ headers文件分布(按字母排序)

CygnusC++ 2.91 for Windows安装于磁盘目录C:\cygnus。图1-2是 这个版本的所有头文件,置千C:\cygnus\cygwin-b20\include\g++,共128个文件,773,042 bytes,下面只是部分:
在这里插入图片描述

其中子目录[std]内有8个文件,70,669bytes:
在这里插入图片描述

1.8.2 SGI STL 文件分布与简介

上面所呈现的 头文件,概略可以分为五组:

  1. C++标准规范下的C头文件(无扩展名),例如cstdio, cstdlib, cstring…
  2. C++标准程序库中不属于STL范畴者, 例如stream, string … 相关文件。
  3. C++标准头文件(无扩展名),例如vector, deque, list, map, algorithm,functional •••
  4. Standard定案前,HP所规范的STL头文件,例如vector.h, deque.h, list.h, map.h, algo.h, function.h…
  5. SGI STL内部文件(STL真止实现于此),例如stl_vector.h,stl_deque.h,stl_list.h, stl_rnap.h, stl_algo.h, stl_function.h … 其中前两组不在本书讨论范围内。后三组头文件详细列表于下。

(1) STL标准头文件(无扩展名)

请注意,各文件之“本书章节”栏如未列出章节号码,表示其实际功能由“说明栏中的stl_xxx取代,因此,实际剖析内容应观察对应之stl_xxx文件所在章节,见稍后之第三列表。
在这里插入图片描述
(2) C++ Standard定案前,HP规范的STL头文件(扩展名.h)
请注意,各文件之本书章节栏如未列出章节号码,表示其实际功能由说明栏中的stl_xxx取代,因此,实际剖析内容应观察对应之s已_xxx文件所在章节,见稍后之第三列表。
在这里插入图片描述
(3) SGI STL内部私用文件CSGISTL真正实现千此)
在这里插入图片描述

1.8.3 GISTL的编译器组态设置(configuration)

不同的编译器对C++语言的支持程度不尽相同。作为一个希望具备广泛移植能力的程序库,SGI STL准备了一个环境组态文件<Stl_config.h>,其中定义了许多常量,标示某些组态的成立与否。所有STL头文件都会直接或间接包含这个组 态文件, 并以条件式写法, 让预处理器(pre-processor)根据各个常量决定取舍哪一段程序代码。 例如:
在这里插入图片描述
<stl_config.h>文件起始处有一份常量定义说明,然后即针对各家不同的编 译器以及可能的不同版本。

stl_config .h中的各种组态(configurations):
在这里插入图片描述

1.9 可能令你困惑的C++语法

1.8所列出的几个组态常量,用来区分编译器对C++Standard的支持程度。这几个组态,也正是许多程序员对千C++语法最为困扰之所在。

1.9.1 stl_config .h中的各种组态(configurations)

以下所列组态编号与上一节所列的<stl_config.h>文件起头的注释编号相同。

组态3:__ STL_ST A TIC_TEMPLA TE_M EMBER_BUG

//file: lconfig3.cppp
//测试在class template中拥有static data members. 
// test__STLSTATIC_TEMPLATE_MEMBER_BUG, defined in <stl_config.h> // ref. C++ Primer 3/e, p.839 
// vc6[o) cb4[x) gcc[o) 
// cb4 does not support static data member ini七ializa巨on.
#include <iostrearn> 
using narnespace std; 
template <typename T> 
class testClass { 
public://纯粹为了方便测试,使用public
static int _data;
};

//为static data members进行定义(配置内存),并设初值 int testClass<int>::_data = 1; 
int testClass<char>::_data = 2; 
int main() 
{
//以下,CB4表现不佳,没有接受初值设定
cout << testClass<int>::_data << endl; //GCC, VC6: 1 CB4: 0 
cout << testClass<char>::_data << endl; //GCC, VC6: 2 CB4: 0 
testClass<int> obj il, obj i2;
testClass<char> obj cl, obj c2;  

cout << obji1._data << endl; 
cout << obji2._data<<endl; 
cout << objc1._data << endl; 
cout << objc2._data << endl;  
obj il._data = 3; 
objc2._data = 4;
cout << obji1._data << endl;
cout<<obji2._data << endl;
cout << objc1._data << endl;
out << objc2._data<< endl;
}

组态5: _ _STL_CLASS_PARTIAL_SPECIA LIZA TION

// file: lconfig5.cpp 
//测试class template partial specialization:在class template的
// 一般化设计之外,特别针对某些template参数做特殊设计
// test _STL_CLASS_PARTIAL_SPECIALIZATION in <stl_config.h> // ref. C++ Primer 3/e, p.860 
// vc6[x) cb4[o] gcc[o]
#include <iostream>
using namespace std;
//一般化设计
template <Class I, class O> 
struct test Class
{ 
testClass(){ cout << "I, O" << endl;}
};

//特殊化设计
template <class T>
struct testClass<T*,T*>
{
testClass(){cout<<"T*,T*"<<endl;}
};
//特殊化设计
template <class T> 
struct testClass<const T*, T*> 
{
testClass(){ cout <<"const T*, T*"<< endl; }
};
int main()
{
testClass<int, char> obj1; //I O
testClass<int*, int*> obj2; //T* T*
testClass<const int*,int*> obj3;//const T*,T*
}

组态6: __ STL_FUNCTION _ TMPL_PARTIAL_ORDER

//file:lconfig6.cpp 
// test _STL_FUNCTION_TMPL_PARTIAL_ORDER in <stl_config.h>
 // vc6[x] cb4[o] gcc[o] 
#include <iostream>
using namespace std; 
class alloc{; 
template <class T, class Alloc = alloc> 
class vector { 
public: 
void swap(vector<T, Alloc>&) { cout << "swap()" << endl; }
};
#ifdef _STL_FUNCTION_TMPL_PARTIAL_ORDER //只为说明,非本程序内容
template<class T, class Alloc>
inline x.swap(y); 
void swap(vector<T, Alloc>& x, vector<T, Alloc>& y) {
x.swap(y);
}
#endif //只为说明,非本程序内容
//以上节录自stl_vector.h,灰色部分系源代码中的条件编译,非本测试程序内容 int main() 
{ 
vector<int> x,y; swap(x, y); 
}

组态7:__ STL_EXPLICIT _FUNCTION _TMPL_ARGS

整个SGISTL内都没有用到这一常量定义。

组态8:__ STL_M EMBER_TEMPLA TES

// file: lconfig8.cpp 
//测试class template之内可否再有template(members) //test__STL MEMBER_TEMPLATES in <Stl_config.h>
 // ref. C++ Primer 3/e, p.844
// ve6 [o] eb4 [o] gee [o] 
#include <iostream> 
using namespace std; 
class alloc { 
) ; 
template <class T, class Alloc = alloc> 
class vector{ 
public: 
typedef T value_type; 
typedef value_type* iterator; 
template <class I> 
void insert(iterator position,I first,I last){
cout << "insert()" << endl;
}
};

int main (){
int ia[5] = {0,1,2,3,4); 
vector<int>x; 
vector<int>::iterator ite; 
x.insert(ite, ia, ia+S); // insert()
}

组态10:_STL_LIMITED_DEFA ULT TEMPLATES

// file: lconfiglO.cpp 
//测试template参数可否根据前一个template参数而设定默认值
//test_STL_LIMITED_DEFAULT_TEMPLATESin <stl_config.h> // ref. C++ Primer 3/e, p.816 
// vc6[o] cb4[o] gcc[o]
#include <iostream> 
#include <cstddef>// for size_t
using namespace std; 
class alloc { 
};
template <class T, class Alloc = alloc, size_t BufSiz = 0> 
class deque{ 
public: 
deque() { cout << "deque" << endl; }
};
//根据前一个参数值T,设定下一个参数Sequence的默认值为deque<T>
template <class T, class Sequence=deque<T> > 
class stack { 
public: 
stack(){ cout << "stack" << endl; } 
private: 
Sequence c; 
);
int main()
{
stack<int> x;// deque 
// stack
};

组态11:__ STL_NON _ TYPE_ TMPL_PARAM_BUG

 // file: lconfigll.cpp 
//测试class template可否拥有non-type template参数
// test _STL_NON_TYPE_TMPL_PARAM_BUG in <Stl_config.h> 
// ref. C++ Primer 3/e, p.825 
// vc6[o] cb4[o] gcc[o] 
#include <iostream> 
#include <cstddef> 
using namespace std; 
class alloc{ 
};
inline size_t _deque_buf_size(size_t n,size_t sz) 
{
return n != O ? n : (sz < 512 ? size_t(512/sz) : size_t(1));
}
template <class T, class Ref, class Ptr, size_t BufSiz> 
struct_deque_iterator{ 
typedef _deque_iterator<T,T&, T*, BufSiz> iterator; 
typedef _deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
 static size_t buffer_size() {return _deque_buf_size(BufSiz, sizeof(T)); }
 }; 
template<class T, class Alloc = alloc, size_t BufSiz = 0> 
class deque { 
public: // Iterators
typedef _deque_iterator<T,T&, T*, BufSiz> iterator;
};
int main()
{
cout << deque<int>::iterator::buffer_size() << endl;// 128 
cout<< deque<int,alloc,64>::iterator::buffer_size() << endl; // 64
}

以下组态常量虽不在前列编号之内,却也是<stl_config.h>内的定义,并使用于整个SGI STL之中,有认识的必要。

组态:__ STL_N ULL_ TMPL_ARGS (bound friend template friend)

<stl_config.h>定义_STL_NULL_TMPL_ARGS如下:

#ifdef _STL_EXPLICIT_FUNCTION_TMPL_ARGS
#define _STL_NULL_TMPL_ARGS <>
#else
#define _STL_NULL_TMPL_ARGS
#endif

这个组态常量常常出现在类似这样的场合(class template的friend函数声明:

// in <stl_stack.h>
template <class T, class Sequence = deque<T> >
class stack{
friend bool operator== _STL_NULL_TMPL_ARGS (const stack&, const stack&);
friend bool operator<_STL_NULL_TMPL_ARGS (const stack&, const stack&);
...
} ;

展开后就变成了:

template <class T, class Sequence = deque<T> >
class stack {
friend bool operator== <> (const stack&, const stack&);
friend bool operator< <> (const stack&, const stack&);
...
} ;

这种奇特的语法是为了实现所谓的bound friend templates, 也就是说class
template的某个具现体(instantiation)与其friend function template的某个具现体
有一对一的关系。下面是一个测试程序:

// file: lconfig-null-template-arguments.cpp
// test _STL_NULL_TMPL_ARGS in <stl_config. h>
// ref. C++ Primer 3le, p.834: bound friend funct ion template
 // vc6[x] cb4[x] gcc[o] 
#include <iostream>
#include <cstddef>//for size_t
using namespace std; 
class alloc{ 
};
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{ 
public: 
deque() { cout << "deque" << ''; } 
};
//以下声明如果不出现, GCC也可以通过。 如果出现, GCC也可以通过。 这一点和C++ Primer 3/e p.834的说法有出入。 书上说一定要有这些前置声明
/*
template <class T, lass Sequence>
class stack;
template <class T,class Sequence>
bool operator==(const stack<T, Sequence>& x,const stack<T, Sequence>& y);
template <class T,class Sequence>
bool operator<(const stack<T, Sequence>& x,const stack<T, Sequence>& y};
*/
template <class T,class Sequence = deque<T> >
class stack {
//写成这样是可以的
friend bool operator==<T> {const stack<T>&, const stack<T>&);
friend bool operator<<T> {const stack<T>&, const stack<T>&);
//写成这样也是可以的
friend bool operator==<T> (const stack&,const stack&);
friend bool operator<<T> (const stack&,const stack&); 
//写成这样也是可以的
friend bool operator==<>(const stack&,const stack&);
friend bool operator<<>(const stack&,const stack&);
//写成这样就不可以
// friend bool operator==(const stack&,const stack&);
// friend bool operator<(const stack&,const stack&);
public:
stack() { cout << "stack”<< endl; }
private:
Sequence c;
};
template <class T, class Sequence> 
bool operator==(const stack<T, Sequence>& x,const stack<T, Sequence>& y) { 
return cout << "operator=="<<'\t'; 
}
template <class T, class Sequence> 
bool operator<(const stack<T,Sequence>& x,const stack<T, Sequence>& y) { 
return cout << "operator<"<<'\t';
}
int main()
{
stack<int> x;// deque stack
 stack<int> y;//deque stack 
 cout << (x == y) << endl; //operator==1 
cout<<(x < y) << endl;// operator<1 
stack<char> yl; //deque stack
// cout << (x==yl) << endl; // error: no match for .. .
// cout << (x < yl) << endl;// error: no matchfor .. . 

组态:__STL TEMPLATE_NULL( class tern p late explicit speciahzation)

<stl_config.h>定义了一个_STL_TEMPLATE_NULL如下:
#ifdef STL CLASS PARTIAL_SPECIALIZATION 
#define _STL_TEMPLATE_NULL template<> 
#else 
#define _STL_TEMPLATE_NULL 
#endif 
这个组态常量常常出现在类似这样的场合:
// in <type_traits.h> 
template<class type> struct_type_traits { ...};
__STL_TEMPLATE_NULL struct _type_traits<char>{ . . . } ;
// in <stl_hash_fun.h> 
template <class Key> struct hash{ };
__STL TEMPLATE_NULL struct hash<char>{...};
__STL TEMPLATE_NULL struct hash<unsigned char>{...};

展开后就变成了:

template<class type>struct_type_traits{ ...};
template<>struct _type_traits<char> { ... };

template<class Key> struct hash { }; 
template<>struct hash<char> { ... };
template<> struct hash<unsigned char> { ... };

这是所谓的class template explicit specialization。下面这个例子适用于GCC和VC6, 允许使用者不指定template<>就完成explicit specialization,C++Builder则是非常严格地要求必须完全遵照C++标准规格,也就是必须明白写出template<>。

// file: lconfig-ternplate-exp-special.cpp 
//以下测试 class template explicit specialization
 //test _STL_TEMPLATE_NULL in <Stl_config.h> 
// ref. C++ Primer 3/e, p.858 
// vc6[o] cb4[x] gcc[o] 
#include <iostream> 
using namespace std;
//将_STL_TEMPLATE_NULL定义为template<>, 可以
 //若定义为blank, 如下, 则只适用于 GCC
#define__STL_TEMPLATE_NULL / * blank * / 
template <class Key> struct hash { 
void operator () () { cout<<"hash<T>"<<endl;}
};
// explicit specialization
__STL_TEMPLATE_NULL struct hash<char> { 
void operator()() { cout<< "hash<char>" << endl; }
};
__STL_TEMPLATE_NULL struct hash<unsigned char> {
void operator()() { cout<< "hash<unsigned char>" << endl; }
};
int main()
{
hash<long> t1; 
hash<char>t2;
hash<unsigned char>t3;
t1() ;//hash<T> 
t2() ; // hash<char> 
t3();// hash<unsigned char>
}

1.9.2 临时对象的产生与运用

临时对象就是一种无名对象(unnamed objects)。它的出现如果不在程序员的预期之下(例如任何pass by value操作都会引发copy操作,于是形成一个临时对象),往往造成效率上的负担。但有时候刻意制造一些临时对象,却是很好的技巧。
制造临时对象的方法:在型别名称之后直接加一 对小括号,并可指定初值,例如Shape(3,5)或int(8),其意义相当于调用相应 的constructor且不指定对象名称。STL最常将此技巧应用于仿函数(functor)与算法的搭配上,例如:

//file: lconfig-temporary-object.cpp 
//本例测试仿函数用于for_each()的情形
 // vc6[o] cb4[o] gcc[o] 
#include <vector> 
#include <algorithm> 
#include <iostream> 
using namespace std; 
template<typename T> 
class print
{
public:
void operator(){const T& elem)//operator重载
{cout<<elem<<‘’;}
};
int main()
{
int ia[6] = { 0, 1, 2, 3, 4, 5 }; 
vector< int > iv(ia, ia+6) ; 
// print<int> ()是一个临时对象,不是一个函数调用操作
for_each(iv.begin(), iv.end(), print<int>());
}

最后一行便是产生"function template具现体"print的一个临时对象。 这个对象将被传人for_each()之中起作用。当for_each()结束时,这个临时对 象也就结束了它的生命。

1.9.4 静态常量整数成员在class 内部直接初始化 (in-class static constant integer initialization )

如果class内含const static integral data member(常量静态整型数据成员), 那么根据 C++ 标准规格,
我们可以在class之内直接给予初值。 所谓integral泛指所有整数型别, 不单只是指int。 下面是 个例子:

// file: lconfig-inclass-init .cpp
//test in-class initialization of static cons七 in七egral members 
// ref. C++ Primer 3/e, p.643 
// vc6[x] cb4[o] gcc[o] 
#include <iostream> 
using namespace std; 
template <typename T> 
class testClass{ 
public: // expedient 
static const int _datai=5;
 static const long _datal =3L;
 static const char _datac='c';
 }

1.9.4 increment/ decrement/ dereference操作符

increment/ dereference操作符在迭代器的实现上占有非常重要的地位,因为任何一个迭代器都必须实现出前进(increment, operator++)和取值(dereference,operator*)功能, 前者还分为前置式(prefix)和后置式(postfix)两种, 有些迭代器具备双向移动功能,那么就必须再提供decrement操作符(也分前置式和后置式两种)。下面是 个范例:

// file: lconfig-operator-overloading.cpp 
// vc6[x) cb4[o] gcc[o] 
// vc6的friend机制搭配C++标准程序库,有bug
#include <iostrearn> 
using namespace std;
class INTfriend ostream& operator<<(ostream&os, const INT& i); 
public: 
INT(int i) : m_i(i){ } ; 
//prefix: increment and then fetch 
INT& operator++() 
{
++(this->m_i); //随着class的不同,该行应该有不同的操作 
return *this; 
}
// postfix : fetch and then incrernent 
const INT operator++(int) 
{
INT temp=*this;
 ++(*this);
return temp; 
}
// prefix : decrernentandthen fetch 
INT& operator--() 
{
--(this->rn_i);//随着class的不同,该行应该有不同的操作 return *this; 
}
//postfix: fetch and then decrement 
const INT operator--(int) 
{
INT temp=*this; 
--(*this);
return temp; 
}
// dereference 
int&operator*() const
{
return (int&) m_i; 
//以上转换操作告诉编译器,你确实要将const int转为non-const 1value.
//如果没有这样明白地转型,有些编译器会给你警告,有些更严格的编译器会视为错误
}
private:
int m_i;
};
ostream& operator<<(ostream&os, const INT&i) 
{
os <<'['<< i.m_i <<'l'; 
return os; 
}
int main()
{
INT I(5); 
cout << I++; //[5]
cout<< ++I;//[7]
cout << I--; //[7]
cout << --I; //[5]
cout << *I; //5
}

1.9.5 前闭后开区间表示法[)

任何一个STL算法, 都需要获得由 一对迭代器(泛型指针)所标示的区间, 用以表示操作范围。这一对迭代器所标示的是个所谓的前闭后开区间 , 以[first, last)表示。 也就是说,整个实际范围从first开始, 直到last-1。 迭代器last所指的是最后一个元素的下一位置”。这种off by one (偏移一格,或说pass the end)的标示法,带来了许多方便,例如下面两个STL算法的循环设计,就显得干净利落:

template <class InputIterator, class T> 
Inputiterator find(Inputiterator first, InputIterator last, const T& value) {
while (first!= last && *first != value) ++first;
return first; 
}
template<class Inputiterator, class Function> 
Function for_each(Inputiterator first, InputIterator last,Function f) {
for (; first != last; ++first)
f(*first); 
return f; 
}

前闭后开区间图示如下(注意,元素之间无需占用连续内存空间):在这里插入图片描述

1.9 .6 function call操作符(operator())

函数调用操作(C++语法中的左右小括号)也可以被重载。许多STL算法都提供了两个版本:
一个用于一般状况(例如排序时以递增方式排列),
一个用于特殊状况(例如排序时由使用者指定以何种特殊关系进行排列)。
像这种情况,需要用户指定某个条件或某个策略,而条件或策略的背后由一整组操作构成,便需要某种特殊的东西来代表这“一整组操作"。
代表”一整组操作”的,当然是函数。过去C语言时代,欲将函数当做参数传递,唯有通过函数指针(pointerto function, 或称function pointer)才能达成,例如:

// file: lqsort.cpp 
#include <cstdlib>
#include <iostream> 
using namespace std; 
int fcmp(const void* elem1, const void* elem2); 
void main() 
{
int ia[lO] = {32,92,67,58,10,4,25,52,59,54};
for(int i=0;i<10;i++)
cout<<ia[i]<<" ";// 32 92 67 58 10 4 25 52 59 54
qsort(ia,sizeof(ia)/sizeof(int) ,s1.zeof(int), fcmp);
for(int i=O; i < 10; i++)
cout << ia [ i ]<< " ";// 4 10 25 32 52 54 58 59 67 92
}
int fcmp(const void* elem1, const void*elem2)
{
const int* il = (const int*)eleml; 
const int* i2 = (const int*)elem2; 
if (*il < *i2) 
return -1; 
else if (*il == *i2) 
return O; 
else if(*il > *i2) 
return 1;
}

但是函数指针有缺点,最重要的是它无法持有自己的状态(所谓局部状态,local states) , 也无法达到组件技术中的可适配性(adapt ability)-也就是无法再将某些修饰条件加诸于其上而改变其状态。

为此,STL算法的特殊版本所接受的所谓“条件”或“策略”或“一整组操作 ,都以仿函数形式呈现。所谓仿函数(functor)就是使用起来像函数一样的东西,如果你针对某个class进行operator()重载,它就成为一个仿函数。至于要 成为一个可配接的仿函数,还需要做一些额外的努力。

下面是一个将operator()重载的例子:

// file: lfunctor.cpp 
#include <iostream> 
using namespace std; 
//由于将operator()重载了,因此plus成了一个仿函数 template <class T> 
struct plus{ 
T operator() (const T&x, const T&y) const{ return x + y; }
};
//由于将operator()重载了,因此minus成了一个仿函数 template <class T> 
struct minus { 
T operator() (const T& x, const T& y) const{ return x -y; }
};
int main()
{
//以下产生仿函数对象
plus<int> plusobj;
minus<int>minusobj;
//以下使用仿函数,就像使用一般函数一样
cout << plusobj (3,5) << endl;//8
cout<< minusobj (3,5) << endl;//-2

//以下直接产生仿函数的临时对象(第一对小括号),并调用之(第二对小括号)
cout<<plus<int>()(43,50)<<endl;//93
cout <<  plue<int>()(43,50) << endl;//-7 
}

上述的plus和minus已经非常接近STL的实现了,唯一的差别在于它缺乏”可配接能力”。关于“可配接能力”,将在第8章详述。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值