??xml version="1.0" encoding="utf-8" standalone="yes"?>
当你不确定编译器的默认charcdӞq昄声明Qsigned char和unsigned charQ?br>
在C/C++语言中,char变量Z个字节,8位,signed char表示的范_-128~127?128在内存中的二q制表示?000 0000Q?27在内存中的表CZؓ0111 1111】;unsign char表示的范_0000 0000~1111 1111Q即0~255Q?br>
注意Q整数在内存中是以补码存取的Q正数的补码Q等于自己,负数的补码:取反?Q例如:127在内存中表示?111 1111Q?-127在内存中表示为~(0111 1111)+1=1000 0001Q?假定某内存单元p的内Ҏ1111 1111Q那么它一定是255吗?实际上取决于你的代码是要把它看成有符可是无W号敎ͼ如果是无W号则表C?55Q如果是有符号则表示-1【对于有W号敎ͼ最高位为符号位Q?表示负,0表示正】:
signed char c=*p; //c=-1
unsigned char c=*p;//c=255
q也解释了上面这D代码能判断~译器默认charcd?br>
在C++中,库的C是非帔R的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的a论。现实中QC++的库门类J多Q解决的问题也是极其q泛Q库从轻量到重量的都有。不都是让人眼界大开Q亦或是望而生叹的思维C。由于库的数量非常庞大,而且限于W者水qI其中很多q不了解。所以文中所提的一些库都是比较著名的大型库?/p>
详细库介l请参考网址Q?a >http://blog.csdn.net/dbafans/archive/2010/06/16/5673114.aspx
contact:karymay@163.net
STL概述
STL的一个重要特Ҏ数据l构和算法的分离。尽这是个单的概念Q但q种分离实使得STL变得非常通用。例如,׃STL的sort()函数是完全通用的,你可以用它来操作几乎M数据集合Q包括链表,容器和数l?/p>
要点
STL法作ؓ模板函数提供。ؓ了和其他lg相区别,在本书中STL法以后接一对圆括弧的方式表C,例如sort()?/p>
STL另一个重要特性是它不是面向对象的。ؓ了具有够通用性,STL主要依赖于模板而不是封装,l承和虚函数Q多态性)——OOP的三个要素。你在STL中找不到M明显的类l承关系。这好像是一U倒退Q但q正好是使得STL的组件具有广泛通用性的底层特征。另外,׃STL是基于模板,内联函数的用得生成的代码短小高效?/p>
提示
保在编译用了STL的程序中臛_要?O优化来保证内联扩展。STL提供了大量的模板cd函数Q可以在OOP和常规编E中使用。所有的STL的大U?0个算法都是完全通用的,而且不依赖于M特定的数据类型。下面的节说明了三个基本的STLlgQ?/p>
1Q?nbsp; q代器提供了讉K容器中对象的Ҏ。例如,可以使用一对P代器指定list或vector中的一定范围的对象。P代器如同一个指针。事实上QC++的指针也是一UP代器。但是,q代器也可以是那些定义了operator*()以及其他cM于指针的操作W地Ҏ的类对象?/p>
2Q?nbsp; 容器是一U数据结构,如listQvectorQ和deques Q以模板cȝҎ提供。ؓ了访问容器中的数据,可以使用由容器类输出的P代器?/p>
3Q?nbsp; 法是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据q行排序Q用find()来搜索一个list中的对象。函数本w与他们操作的数据的l构和类型无养I因此他们可以在从单数l到高度复杂容器的Q何数据结构上使用?/p>
头文?br>Z避免和其他头文g冲突Q?STL的头文g不再使用常规?h扩展。ؓ了包含标准的stringc,q代器和法Q用下面的指C符Q?/p>
#include <string>
#include <iterator>
#include <algorithm>
如果你查看STL的头文gQ你可以看到象iterator.h和stl_iterator.hq样的头文g。由于这些名字在各种STL实现之间都可能不同,你应该避免用这些名字来引用q些头文件。ؓ了确保可UL性,使用相应的没?h后缀的文件名。表1列出了最怋用的各种容器cȝ头文件。该表ƈ不完_对于其他头文Ӟ我将在本章和后面的两章中介绍?/p>
?1. STL头文件和容器c?/p>
#include
Container Class
<deque>
deque
<list>
list
<map>
map, multimap
<queue>
queue, priority_queue
<set>
set, multiset
<stack>
stack
<vector>
vector, vector<bool>
名字I间
你的~译器可能不能识别名字空间。名字空间就好像一个信,标志符装在另一个名字中。标志符只在名字I间中存在,因而避免了和其他标志符冲突。例如,可能有其他库和程序模块定义了sort()函数Qؓ了避免和STL地sort()法冲突QSTL的sort()以及其他标志W都装在名字空间std中。STL的sort()法~译为std::sort()Q从而避免了名字冲突?/p>
管你的~译器可能没有实现名字空_你仍然可以用他们。ؓ了用STLQ可以将下面的指C符插入C的源代码文g中,典型地是在所有的#include指示W的后面Q?/p>
using namespace std;
q代?br>q代器提供对一个容器中的对象的讉KҎQƈ且定义了容器中对象的范围。P代器如同一个指针。事实上QC++的指针也是一UP代器。但是,q代器不仅仅是指针,因此你不能认Z们一定具有地址倹{例如,一个数l烦引,也可以认为是一UP代器?/p>
q代器有各种不同的创建方法。程序可能把q代器作Z个变量创建。一个STL容器cd能ؓ了用一个特定类型的数据而创Z个P代器。作为指针,必须能够使用*操作W类获取数据。你q可以用其他数学操作符?+。典型的Q?+操作W用来递增q代器,以访问容器中的下一个对象。如果P代器到达了容器中的最后一个元素的后面Q则q代器变成past-the-end倹{用一个past-the-end值得指针来访问对象是非法的,好像用NULL或ؓ初始化的指针一栗?/p>
提示
STL不保证可以从另一个P代器来抵达一个P代器。例如,当对一个集合中的对象排序时Q如果你在不同的l构中指定了两个q代器,W二个P代器无法从第一个P代器抵达Q此时程序注定要p|。这是STL灉|性的一个代仗STL不保证检毫无道理的错误?/p>
q代器的cd
对于STL数据l构和算法,你可以用五UP代器。下面简要说明了q五U类型:
· Input iterators 提供Ҏ据的只读讉K?/p>
· Output iterators 提供Ҏ据的只写讉K
· Forward iterators 提供d操作Qƈ能向前推qP代器?/p>
· Bidirectional iterators提供d操作Qƈ能向前和向后操作?/p>
· Random access iterators提供d操作Qƈ能在数据中随机移动?/p>
管各种不同的STL实现l节斚w有所不同Q还是可以将上面的P代器惌ZU类l承关系。从q个意义上说Q下面的q代器承自上面的P代器。由于这U承关p,你可以将一个Forwardq代器作Z个output或inputq代器用。同P如果一个算法要求是一个bidirectional q代器,那么只能使用该种cd和随问P代器?
指针q代?br>正如下面的小E序昄的,一个指针也是一UP代器。该E序同样昄了STL的一个主要特性——它不只是能够用于它自己的类cdQ而且也能用于MC或C++cd。Listing 1, iterdemo.cpp, 昄了如何把指针作ؓq代器用于STL的find()法来搜索普通的数组?/p>
?1. iterdemo.cpp
#include <iostream.h>
#include <algorithm>
using namespace std;
#define SIZE 100
int iarray[SIZE];
int main()
{
iarray[20] = 50;
int* ip = find(iarray, iarray + SIZE, 50);
if (ip == iarray + SIZE)
cout << "50 not found in array" << endl;
else
cout << *ip << " found in array" << endl;
return 0;
}
在引用了I/O库和STL法头文Ӟ注意没有.h后缀Q,该程序告诉编译器使用std名字I间。用std名字I间的这行是可选的Q因为可以删除该行对于这么一个小E序来说不会D名字冲突?/p>
E序中定义了寸为SIZE的全局数组。由于是全局变量Q所以运行时数组自动初始化ؓ零。下面的语句在索引20位置处地元素讄?0,q用find()法来搜索?0:
iarray[20] = 50;
int* ip = find(iarray, iarray + SIZE, 50);
find()函数接受三个参数。头两个定义了搜索的范围。由于C和C++数组{同于指针,表达式iarray指向数组的第一个元素。而第二个参数iarray + SIZE{同于past-the-end |也就是数l中最后一个元素的后面位置。第三个参数是待定位的|也就?0。find()函数q回和前两个参数相同cd的P代器Q这儿是一个指向整数的指针ip?
提示
必须CSTL使用模板。因此,STL函数自动Ҏ它们使用的数据类型来构造?/p>
Z判断find()是否成功Q例子中试ip?past-the-end 值是否相{:
if (ip == iarray + SIZE) ...
如果表达式ؓ真,则表C在搜烦的范围内没有指定的倹{否则就是指向一个合法对象的指针Q这时可以用下面的语句显C::
cout << *ip << " found in array" << endl;
试函数q回值和NULL是否相等是不正确的。不要象下面q样使用Q?/p>
int* ip = find(iarray, iarray + SIZE, 50);
if (ip != NULL) ... // ??? incorrect
当用STL函数Ӟ只能试ip是否和past-the-end 值是否相{。尽在本例中ip是一个C++指针,其用法也必须W合STLq代器的规则?/p>
容器q代?br>管C++指针也是q代器,但用的更多的是容器P代器。容器P代器用法和iterdemo.cpp一P但和P代器x为指针变量不同的是,你可以用容器类Ҏ来获取P代器对象。两个典型的容器cL法是begin()和end()。它们在大多数容器中表示整个容器范围。其他一些容器还使用rbegin()和rend()Ҏ提供反向q代器,以按反向序指定对象范围?/p>
下面的程序创Z一个矢量容器(STL的和数组{h的对象)Qƈ使用q代器在其中搜烦。该E序和前一章中的程序相同?/p>
Listing 2. vectdemo.cpp
#include <iostream.h>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> intVector(100);
void main()
{
intVector[20] = 50;
vector<int>::iterator intIter =
find(intVector.begin(), intVector.end(), 50);
if (intIter != intVector.end())
cout << "Vector contains value " << *intIter << endl;
else
cout << "Vector does not contain 50" << endl;
}
注意用下面的Ҏ昄搜烦到的数据Q?/p>
cout << "Vector contains value " << *intIter << endl;
帔Rq代?br>和指针一P你可以给一个P代器赋倹{例如,首先x一个P代器Q?/p>
vector<int>::iterator first;
该语句创Z一个vector<int>cȝq代器。下面的语句该q代器设|到intVector的第一个对象,q将它指向的对象D|ؓ123Q?
first = intVector.begin();
*first = 123;
q种赋值对于大多数容器c都是允许的Q除了只d量。ؓ了防止错误赋|可以xq代器ؓQ?/p>
const vector<int>::iterator result;
result = find(intVector.begin(), intVector.end(), value);
if (result != intVector.end())
*result = 123; // ???
警告
另一U防止数据被改变得方法是容器申明ؓconstcd?/p>
『呀Q在VC中测试出?正确的含义是result成ؓ帔R而不是它指向的对象不允许改变Q如同int *const p;看来q作者自׃不懂?
使用q代器编E?br>你已l见Cq代器的一些例子,现在我们关注每U特定的q代器如何用。由于用P代器需要关于STL容器cd法的知识,在阅M后面的两章后你可能需要重新复习一下本章内宏V?/p>
输入q代?br>输入q代器是最普通的cd。输入P代器臛_能够使用==?=试是否相等Q?来访问数据;使用++操作来递推q代器到下一个元素或到达past-the-end 倹{?/p>
Z理解q代器和STL函数是如何用它们的Q现在来看一下find()模板函数的定义:
template <class InputIterator, class T>
InputIterator find(
InputIterator first, InputIterator last, const T& value) {
while (first != last && *first != value) ++first;
return first;
}
注意
在find()法中,注意如果first和last指向不同的容器,该算法可能陷入死循环?/p>
输出q代?br>输出q代器缺省只写,通常用于数据从一个位|拷贝到另一个位|。由于输P代器无法d对象Q因此你不会在Q何搜索和其他法中用它。要惌取一个拷贝的|必须使用另一个输入P代器Q或它的l承q代器)?/p>
Listing 3. outiter.cpp
#include <iostream.h>
#include <algorithm> // Need copy()
#include <vector> // Need vector
using namespace std;
double darray[10] =
{1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};
vector<double> vdouble(10);
int main()
{
vector<double>::iterator outputIterator = vdouble.begin();
copy(darray, darray + 10, outputIterator);
while (outputIterator != vdouble.end()) {
cout << *outputIterator << endl;
outputIterator++;
}
return 0;
}
注意
当用copy()法的时候,你必ȝ保目标容器有_大的I间Q或者容器本w是自动扩展的?/p>
前推q代?br>前推q代器能够读写数据|q能够向前推q到下一个倹{但是没法递减。replace()法昄了前推P代器的用方法?
template <class ForwardIterator, class T>
void replace (ForwardIterator first,
ForwardIterator last,
const T& old_value,
const T& new_value);
使用replace()[first,last]范围内的所有gؓold_value的对象替换ؓnew_value?
replace(vdouble.begin(), vdouble.end(), 1.5, 3.14159);
双向q代?br>双向q代器要求能够增减。如reverse()法要求两个双向q代器作为参?
template <class BidirectionalIterator>
void reverse (BidirectionalIterator first,
BidirectionalIterator last);
使用reverse()函数来对容器q行逆向排序:
reverse(vdouble.begin(), vdouble.end());
随机讉Kq代?br>随机讉Kq代器能够以L序讉K数据Qƈ能用于读写数据(不是const的C++指针也是随机讉Kq代器)。STL的排序和搜烦函数使用随机讉Kq代器。随问P代器可以使用关系操作W作比较?/p>
random_shuffle() 函数随机打ؕ原先的顺序。申明ؓQ?/p>
template <class RandomAccessIterator>
void random_shuffle (RandomAccessIterator first,
RandomAccessIterator last);
使用ҎQ?/p>
random_shuffle(vdouble.begin(), vdouble.end());
q代器技?br>要学会用P代器和容器以及算法,需要学习下面的新技术?/p>
和q代?br>本书的很多例子程序用I/O语句来d数据。例如:
int value;
cout << "Enter value: ";
cin >> value;
cout << "You entered " << value << endl;
对于q代器,有另一U方法用流和标准函数。理解的要点是将输入/输出作为容器看待。因此,M接受q代器参数的法都可以和一起工作?
Listing 4. outstrm.cpp
#include <iostream.h>
#include <stdlib.h> // Need random(), srandom()
#include <time.h> // Need time()
#include <algorithm> // Need sort(), copy()
#include <vector> // Need vector
using namespace std;
void Display(vector<int>& v, const char* s);
int main()
{
// Seed the random number generator
srandom( time(NULL) );
// Construct vector and fill with random integer values
vector<int> collection(10);
for (int i = 0; i < 10; i++)
collection[i] = random() % 10000;;
// Display, sort, and redisplay
Display(collection, "Before sorting");
sort(collection.begin(), collection.end());
Display(collection, "After sorting");
return 0;
}
// Display label s and contents of integer vector v
void Display(vector<int>& v, const char* s)
{
cout << endl << s << endl;
copy(v.begin(), v.end(),
ostream_iterator<int>(cout, "\t"));
cout << endl;
}
函数Display()昄了如何用一个输出流q代器。下面的语句容器中的g输到cout输出对象中:
copy(v.begin(), v.end(),
ostream_iterator<int>(cout, "\t"));
W三个参数实例化了ostream_iterator<int>cdQƈ它作ؓcopy()函数的输出目标P代器对象?#8220;\t”字符串是作ؓ分隔W。运行结果:
$ g++ outstrm.cpp
$ ./a.out
Before sorting
677 722 686 238 964 397 251 118 11 312
After sorting
11 118 238 251 312 397 677 686 722 964
q是STL奇的一面『确实神奇』。ؓ定义输出P代器QSTL提供了模板类ostream_iterator。这个类的构造函数有两个参数Q一个ostream对象和一个string倹{因此可以象下面一L单地创徏一个P代器对象Q?/p>
ostream_iterator<int>(cout, "\n")
该P代v可以和Q何接受一个输P代器的函C起用?/p>
插入q代?br>插入q代器用于将值插入到容器中。它们也叫做适配器,因ؓ它们容器适配或{化ؓ一个P代器Qƈ用于copy()q样的算法中。例如,一个程序定义了一个链表和一个矢量容?
list<double> dList;
vector<double> dVector;
通过使用front_inserterq代器对象,可以只用单个copy()语句完成将矢量中的对象插入到链表前端的操作Q?/p>
copy(dVector.begin(), dVector.end(), front_inserter(dList));
三种插入q代器如下:
· 普通插入器 对象插入到容器M对象的前面?/p>
· Front inserters 对象插入到数据集的前面——例如,链表表头?/p>
· Back inserters 对象插入到集合的尾部——例如,矢量的尾部,D矢量容器扩展?/p>
使用插入q代器可能导致容器中的其他对象移动位|,因而得现存的q代器非法。例如,一个对象插入到矢量容器导致其他值移动位|以腑ևI间。一般来_插入到象链表q样的结构中更ؓ有效Q因为它们不会导致其他对象移动?/p>
Listing 5. insert.cpp
#include <iostream.h>
#include <algorithm>
#include <list>
using namespace std;
int iArray[5] = { 1, 2, 3, 4, 5 };
void Display(list<int>& v, const char* s);
int main()
{
list<int> iList;
// Copy iArray backwards into iList
copy(iArray, iArray + 5, front_inserter(iList));
Display(iList, "Before find and copy");
// Locate value 3 in iList
list<int>::iterator p =
find(iList.begin(), iList.end(), 3);
// Copy first two iArray values to iList ahead of p
copy(iArray, iArray + 2, inserter(iList, p));
Display(iList, "After find and copy");
return 0;
}
void Display(list<int>& a, const char* s)
{
cout << s << endl;
copy(a.begin(), a.end(),
ostream_iterator<int>(cout, " "));
cout << endl;
}
q行l果如下Q?/p>
$ g++ insert.cpp
$ ./a.out
Before find and copy
5 4 3 2 1
After find and copy
5 4 1 2 3 2 1
可以front_inserter替换为back_inserter试试?/p>
如果用find()L扑֜列表中不存在的|例如99。由于这时将p讄为past-the-end 倹{最后的copy()函数iArray的值附加到链表的后部?/p>
混合q代器函?br>在涉及到容器和算法的操作中,q有两个q代器函数非常有用:
· advance() 按指定的数目增减q代器?/p>
· distance() q回到达一个P代器所需Q递增Q操作的数目?/p>
例如Q?/p>
list<int> iList;
list<int>::iterator p =
find(iList.begin(), iList.end(), 2);
cout << "before: p == " << *p << endl;
advance(p, 2); // same as p = p + 2;
cout << "after : p == " << *p << endl;
int k = 0;
distance(p, iList.end(), k);
cout << "k == " << k << endl;
advance()函数接受两个参数。第二个参数是向前推q的数目。对于前推P代器Q该值必Mؓ正,而对于双向P代器和随问P代器Q该值可以ؓ负?/p>
使用 distance()函数来返回到辑֏一个P代器所需要的步骤?br>注意
distance()函数是P代的Q也是_它递增W三个参数。因此,你必d始化该参数。未初始化该参数几乎注定要失败?/p>
函数和函数对?br>STL中,函数被称为算法,也就是说它们和标准C库函数相比,它们更ؓ通用。STL法通过重蝲operator()函数实现为模板类或模板函数。这些类用于创徏函数对象Q对容器中的数据q行各种各样的操作。下面的几节解释如何使用函数和函数对象?/p>
函数和断a
l常需要对容器中的数据q行用户自定义的操作。例如,你可能希望遍历一个容器中所有对象的STL法能够回调自己的函数。例?/p>
#include <iostream.h>
#include <stdlib.h> // Need random(), srandom()
#include <time.h> // Need time()
#include <vector> // Need vector
#include <algorithm> // Need for_each()
#define VSIZE 24 // Size of vector
vector<long> v(VSIZE); // Vector object
// Function prototypes
void initialize(long &ri);
void show(const long &ri);
bool isMinus(const long &ri); // Predicate function
int main()
{
srandom( time(NULL) ); // Seed random generator
for_each(v.begin(), v.end(), initialize);//调用普通函?br> cout << "Vector of signed long integers" << endl;
for_each(v.begin(), v.end(), show);
cout << endl;
// Use predicate function to count negative values
//
int count = 0;
vector<long>::iterator p;
p = find_if(v.begin(), v.end(), isMinus);//调用断言函数
while (p != v.end()) {
count++;
p = find_if(p + 1, v.end(), isMinus);
}
cout << "Number of values: " << VSIZE << endl;
cout << "Negative values : " << count << endl;
return 0;
}
// Set ri to a signed integer value
void initialize(long &ri)
{
ri = ( random() - (RAND_MAX / 2) );
// ri = random();
}
// Display value of ri
void show(const long &ri)
{
cout << ri << " ";
}
// Returns true if ri is less than 0
bool isMinus(const long &ri)
{
return (ri < 0);
}
所谓断a函数Q就是返回bool值的函数?/p>
函数对象
除了lSTL法传递一个回调函敎ͼ你还可能需要传递一个类对象以便执行更复杂的操作。这L一个对象就叫做函数对象。实际上函数对象是一个类Q但它和回调函数一样可以被回调。例如,在函数对象每ơ被for_each()或find_if()函数调用时可以保留统计信息。函数对象是通过重蝲operator()()实现的。如果TanyClass定义了opeator()(),那么可以这么用:
TAnyClass object; // Construct object
object(); // Calls TAnyClass::operator()() function
for_each(v.begin(), v.end(), object);
STL定义了几个函数对象。由于它们是模板Q所以能够用于Q何类型,包括C/C++固有的数据类型,如long。有些函数对象从名字中就可以看出它的用途,如plus()和multiplies()。类似的greater()和less-equal()用于比较两个倹{?
注意
有些版本的ANSI C++定义了times()函数对象Q而GNU C++把它命名为multiplies()。用时必须包含头文?lt;functional>?/p>
一个有用的函数对象的应用是accumulate() 法。该函数计算容器中所有值的d。记住这Lg一定是单的cdQ通过重蝲operator+()Q也可以是类对象?/p>
Listing 8. accum.cpp
#include <iostream.h>
#include <numeric> // Need accumulate()
#include <vector> // Need vector
#include <functional> // Need multiplies() (or times())
#define MAX 10
vector<long> v(MAX); // Vector object
int main()
{
// Fill vector using conventional loop
//
for (int i = 0; i < MAX; i++)
v[i] = i + 1;
// Accumulate the sum of contained values
//
long sum =
accumulate(v.begin(), v.end(), 0);
cout << "Sum of values == " << sum << endl;
// Accumulate the product of contained values
//
long product =
accumulate(v.begin(), v.end(), 1, multiplies<long>());//注意q行
cout << "Product of values == " << product << endl;
return 0;
}
~译输出如下Q?/p>
$ g++ accum.cpp
$ ./a.out
Sum of values == 55
Product of values == 3628800
『注意用了函数对象的accumulate()的用法。accumulate() 在内部将每个容器中的对象和第三个参数作ؓmultiplies函数对象的参?multiplies(1,v)计算乘积。VC中的q些模板的源代码如下Q?/p>
// TEMPLATE FUNCTION accumulate
template<class _II, class _Ty> inline
_Ty accumulate(_II _F, _II _L, _Ty _V)
{for (; _F != _L; ++_F)
_V = _V + *_F;
return (_V); }
// TEMPLATE FUNCTION accumulate WITH BINOP
template<class _II, class _Ty, class _Bop> inline
_Ty accumulate(_II _F, _II _L, _Ty _V, _Bop _B)
{for (; _F != _L; ++_F)
_V = _B(_V, *_F);
return (_V); }
// TEMPLATE STRUCT binary_function
template<class _A1, class _A2, class _R>
struct binary_function {
typedef _A1 first_argument_type;
typedef _A2 second_argument_type;
typedef _R result_type;
};
// TEMPLATE STRUCT multiplies
template<class _Ty>
struct multiplies : binary_function<_Ty, _Ty, _Ty> {
_Ty operator()(const _Ty& _X, const _Ty& _Y) const
{return (_X * _Y); }
};
引言Q如果你x入了解STL到底是怎么实现的,最好的办法是写个简单的E序Q将E序中涉及到的模板源码给copy下来Q稍作整理,p看懂了。所以没有必要去C么《STL源码剖析》之cȝ书籍Q那些书可能反而浪Ҏ间。?/p>
发生器函数对?br>有一cL用的函数对象?#8220;发生?#8221;(generator)。这cd数有自己的内存,也就是说它能够从先前的调用中C一个倹{例如随机数发生器函数?/p>
普通的CE序员用静态或全局变量 “记忆”上次调用的结果。但q样做的~点是该函数无法和它的数据相分离『还有个~点是要用TLS才能U程安全』。显Ӟ使用cL装一块:“内存”更安全可靠。先看一下例子:
Listing 9. randfunc.cpp
#include <iostream.h>
#include <stdlib.h> // Need random(), srandom()
#include <time.h> // Need time()
#include <algorithm> // Need random_shuffle()
#include <vector> // Need vector
#include <functional> // Need ptr_fun()
using namespace std;
// Data to randomize
int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> v(iarray, iarray + 10);
// Function prototypes
void Display(vector<int>& vr, const char *s);
unsigned int RandInt(const unsigned int n);
int main()
{
srandom( time(NULL) ); // Seed random generator
Display(v, "Before shuffle:");
pointer_to_unary_function<unsigned int, unsigned int>
ptr_RandInt = ptr_fun(RandInt); // Pointer to RandInt()//注意q行
random_shuffle(v.begin(), v.end(), ptr_RandInt);
Display(v, "After shuffle:");
return 0;
}
// Display contents of vector vr
void Display(vector<int>& vr, const char *s)
{
cout << endl << s << endl;
copy(vr.begin(), vr.end(), ostream_iterator<int>(cout, " "));
cout << endl;
}
// Return next random value in sequence modulo n
unsigned int RandInt(const unsigned int n)
{
return random() % n;
}
~译q行l果如下Q?/p>
$ g++ randfunc.cpp
$ ./a.out
Before shuffle:
1 2 3 4 5 6 7 8 9 10
After shuffle:
6 7 2 8 3 5 10 1 9 4
首先用下面的语句x一个对象:
pointer_to_unary_function<unsigned int, unsigned int>
ptr_RandInt = ptr_fun(RandInt);
q儿使用STL的单目函数模板定义了一个变量ptr_RandIntQƈ地址初始化到我们的函数RandInt()。单目函数接受一个参敎ͼq返回一个倹{现在random_shuffle()可以如下调用Q?/p>
random_shuffle(v.begin(), v.end(), ptr_RandInt);
在本例子中,发生器只是简单的调用rand()函数?br>
关于帔R引用的一点小ȝQ不译了,VC下将例子中的constLQ?
发生器函数类对象
下面的例子说明发生器函数cd象的使用?/p>
Listing 10. fiborand.cpp
#include <iostream.h>
#include <algorithm> // Need random_shuffle()
#include <vector> // Need vector
#include <functional> // Need unary_function
using namespace std;
// Data to randomize
int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> v(iarray, iarray + 10);
// Function prototype
void Display(vector<int>& vr, const char *s);
// The FiboRand template function-object class
template <class Arg>
class FiboRand : public unary_function<Arg, Arg> {
int i, j;
Arg sequence[18];
public:
FiboRand();
Arg operator()(const Arg& arg);
};
void main()
{
FiboRand<int> fibogen; // Construct generator object
cout << "Fibonacci random number generator" << endl;
cout << "using random_shuffle and a function object" << endl;
Display(v, "Before shuffle:");
random_shuffle(v.begin(), v.end(), fibogen);
Display(v, "After shuffle:");
}
// Display contents of vector vr
void Display(vector<int>& vr, const char *s)
{
cout << endl << s << endl;
copy(vr.begin(), vr.end(),
ostream_iterator<int>(cout, " "));
cout << endl;
}
// FiboRand class constructor
template<class Arg>
FiboRand<Arg>::FiboRand()
{
sequence[17] = 1;
sequence[16] = 2;
for (int n = 15; n > 0; n?
sequence[n] = sequence[n + 1] + sequence[n + 2];
i = 17;
j = 5;
}
// FiboRand class function operator
template<class Arg>
Arg FiboRand<Arg>::operator()(const Arg& arg)
{
Arg k = sequence[i] + sequence[j];
sequence[i] = k;
i--;
j--;
if (i == 0) i = 17;
if (j == 0) j = 17;
return k % arg;
}
~译q行输出如下:
$ g++ fiborand.cpp
$ ./a.out
Fibonacci random number generator
using random_shuffle and a function object
Before shuffle:
1 2 3 4 5 6 7 8 9 10
After shuffle:
6 8 5 4 3 7 10 1 9
该程序用完全不通的Ҏ使用使用rand_shuffle。Fibonacci 发生器封装在一个类中,该类能从先前?#8220;使用”中记忆运行结果。在本例中,cFiboRand l护了一个数l和两个索引变量I和j?/p>
FiboRandcȝ承自unary_function() 模板:
template <class Arg>
class FiboRand : public unary_function<Arg, Arg> {...
Arg是用戯定义数据cd。该c还定以了两个成员函敎ͼ一个是构造函敎ͼ另一个是operator()Q)函数Q该操作W允许random_shuffle()法象一个函C?#8220;调用”一个FiboRand对象?
l定器函数对?br>一个绑定器使用另一个函数对象f()和参数值V创徏一个函数对象。被l定函数对象必须为双目函敎ͼ也就是说有两个参?A和B。STL 中的帮定器有Q?/p>
· bind1st() 创徏一个函数对象,该函数对象将值V作ؓW一个参数A?/p>
· bind2nd()创徏一个函数对象,该函数对象将值V作ؓW二个参数B?/p>
举例如下Q?/p>
Listing 11. binder.cpp
#include <iostream.h>
#include <algorithm>
#include <functional>
#include <list>
using namespace std;
// Data
int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
list<int> aList(iarray, iarray + 10);
int main()
{
int k = 0;
count_if(aList.begin(), aList.end(),
bind1st(greater<int>(), 8), k);
cout << "Number elements < 8 == " << k << endl;
return 0;
}
Algorithm count_if()计算满特定条g的元素的数目?q是通过一个函数对象和一个参数捆l到Z个对象,q将该对象作为算法的W三个参数实现的?注意q个表达?
bind1st(greater<int>(), 8)
该表辑ּgreater<int>()和一个参数?捆绑Z个函数对象。由于用了bind1st()Q所以该函数相当于计下q表辑ּQ?
8 > q
表达式中的q是容器中的对象。因此,完整的表辑ּ
count_if(aList.begin(), aList.end(),
bind1st(greater<int>(), 8), k);
计算所有小于或{于8的对象的数目?
否定函数对象
所谓否?negator)函数对象Q就是它从另一个函数对象创来Q如果原先的函数q回真,则否定函数对象返回假。有两个否定函数对象Qnot1()和not2()。not1()接受单目函数对象Qnot2()接受双目函数对象。否定函数对象通常和帮定器一起用。例如,上节中用bind1nd来搜索q<=8的|
count_if(aList.begin(), aList.end(),
bind1st(greater<int>(), 8), k);
如果要搜索q>8的对象,则用bind2st。而现在可以这样写Q?/p>
start = find_if(aList.begin(), aList.end(),
not1(bind1nd(greater<int>(), 6)));
你必M用not1Q因为bind1ndq回单目函数?
ȝQ用标准模板库 (STL)
管很多E序员仍然在使用标准C函数Q但是这好像骑着毛驴LMercedes一栗你当然最l也会到辄标,但是你浪费了很多旉?/p>
管有时候用标准C函数实方便(如用sprintf()q行格式化输?。但是C函数不用异常机制来报告错误Q也不适合处理新的数据cd。而且标准C函数l常使用内存分配技术,没有l验的程序员很容易写出bug来?
C++标准库则提供了更为安全,更ؓ灉|的数据集处理方式。STL最初由HP实验室的Alexander Stepanov和Meng Lee开发。最q,C++标准委员会采U了STLQ尽在不同的实C间仍有细节差别?/p>
STL的最主要的两个特点:数据l构和算法的分离Q非面向对象本质。访问对象是通过象指针一Lq代器实现的Q容器是象链表,矢量之类的数据结构,q按模板方式提供Q算法是函数模板Q用于操作容器中的数据。由于STL以模板ؓ基础Q所以能用于M数据cd和结构?/p>
//*Q最前面?是开养IL后下面这D代码就被注释了Q)
codeSegement1;
//*/
后来Q在我的实践中,我又发现了可以在两段代码间切换的ҎQ?/p>
//*Q最前面?是开养IL后第1D被注释Q第2D|效)
codeSegement1;
/*/
codeSegement2;
//*/
2.NULL定义
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
3. 标准头文件中都有如下l构Q比如stdio.h文gQ:
#ifndef _INC_STDIO
#define _INC_STDIO
#ifdef __cplusplus
extern "C" {
#endif
/*...
.....
*/
#ifdef __cplusplus
}
#endif
#endif /* _INC_STDIO*/