5.5 迭代器之配接器
C++标准程序库提供了多个预先定义的特殊迭代器,即迭代器配接器。下面简述三种迭代器配接器(详见page264)。
5.5.1 insert iterator
插入型迭代器使算法insert方式而非overwrite方式运作,可解决“目标空间不足”的问题。
insert iterator对内部接口做了新定义:
若对某元素进行assign,会引发“对元素所属群集的insert操作”,insert的位置须视三种不同的insert iterator而定:
单步前进(step forward):不会造成任何动静。eg:
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
list coll1;
// insert elements from 1 to 9 into the first collection
for (int i = 1; i <= 9; ++i)
{
coll1.push_back(i);
}
// copy the elements of coll1 into coll2 by appending them
vector coll2;
copy (coll1.begin(), coll1.end(), // source
back_inserter(coll2)); // destination
// copy the elements of coll1 into coll3 by inserting them at the
// front -- reverses the order of the elements
deque coll3;
copy (coll1.begin(), coll1.end(), // sourcce
front_inserter(coll3)); // destination
// copy the elements of coll1 into coll4
// - only inserter that works for associative collections
set coll4;
copy(coll1.begin(), coll1.end(), // source
inserter(coll4,coll4.begin())); // destination
}
上例用了三种预定义的insert iterator:
1. back inserter(安插于容器末端)
back inserter的内部调用push_back()。(当然只有支持push_back()的容器才能用back_inserter())
完成语句copy(coll1.begin(), coll1.end(), back_inserter(coll2));后coll1的所有元素会附加到coll2中。
2.front inserter(安插于容器首部)
front inserter内部调用push_front()。
3. inserter(一般性inserter)
将元素插入“初始化时接受的第二参数”所指位置的前方。
内部调用insert()并以新值和新位置作为参数。
所有STL容器都提供insert()成员函数,故inserter是唯一可用到关联式容器上的insert iterator。
对关联式容器进行insert时,所给的插入位置只是一个提示,帮助它从何开始查找正确的插入位置,若提示不正确,效率上会比“没有提示”更糟糕。
5.5.2 stream iterator
用于“读写”stream的迭代器,是的来自键盘的输入像个群集,能从中读取内容。同理输出结果导出到文件或屏幕。
// stl/ioiter1.cpp
#include
#include
#include // 使用iterator要包含此文件
#include
#include
using namespace std;
int main()
{
vector col1;
/* read all words from the standard input
* -source: all strings until end-of-file (or enter)
* -destination: col1 (inserting)
*/
copy (istream_iterator (cin), // start of source
istream_iterator (), // end of source
back_inserter(col1)); // destination
// sort elements
sort (col1.begin(), col1.end());
/* print all elements without duplicates
* - source: col1
* - destination: standard output (with newline between elements)
*/
unique_copy (col1.begin(), col1.end(), // source
ostream_iterator (cout,"\n")); // destination
}
istream_iterator(cin)会产生一个
可从standard input stream(cin)读取数据的stream iterator。
其中的template参数string表该stream iterator专门读取string型别元素。
这些元素通过operator>>读取进来,每当企图处理下一个元素时,
istream iterator会将这种企图化作 cin>>string;来执行。
note:针对string而执行的input操作符通常读取以空白分隔的文字(详见page492)。
istream_iterator()调用istream iterator的default ctor,
产生一个代表“流结束符号(end-of-stream)”的迭代器,其意义:不能再从中读取任何东西。
- copy()函数中,只要不断逐一前进的第一参数不等于第二参数,copy操作就会继续。
- unique_copy (col1.begin(), col1.end(),ostream_iterator
(cout,"\n")); 将所有元素拷贝到cout端并消除相邻重复值。
其中ostream_iterator(cout,"\n")产生一个output stream iterator,通过operator<<向cout写入string。cout的第二参数(可有可无)用来作为元素之间的分隔符。
5.5.3 reverse iterator(逆向迭代器)(page109)
以逆向方式进行所有操作,将increment/decrement运算转换为decrement/increment运算。所有容器可用成员函数rbegin()和rend()产生reverse iterator,eg:
// stl/riter1.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
vector col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// print all element in reverse order
copy (col1.rbegin(), col1.rend(), ostream_iterator(cout," "));
cout << endl;
}
- rbegin(),指向的群集的结尾位置(即最后元素的下一位置),故*col1.rbegin()返回最后一个元素的值。
- rend(),“对群集元素逆向遍历”的终点,即反方向的“逾尾”位置,指的是容器内的第一个元素的前一个位置。
note: *end()和 *rend()是未定义的。
5.6 更易型算法(manipulating algorithm)
manipulating algorithm指 会删除或重排或修改元素的算法。
5.6.1 removing(移除元素)
- remove()从某区间删除元素。
// remove()删除容器中元素
// stl/remove1.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
list col1;
// insert elements from 6 to 1 and 1 to 6
for (int i = 1; i <= 6; ++i)
{
col1.push_front(i);
col1.push_back(i);
}
// print all elements of the collection
cout << "pre: ";
copy (col1.begin(), col1.end(), ostream_iterator(cout, " "));
cout << endl;
// remove all elements with value 3
remove (col1.begin(), col1.end(), 3);
// print all elements of the collection
cout << "post: ";
copy (col1.begin(), col1.end(), ostream_iterator(cout, " "));
cout << endl;
}
但输出结果:
事实上,remove()返回了一个新终点,可用该终点获得新区间、缩减后的容器大小或获得被删除元素的个数。eg:
// stl/remove2.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
list col1;
// insert elements from 6 to 1 and 1 to 6
for (int i=1; i <= 6; ++i)
{
col1.push_front(i);
col1.push_back(i);
}
// print all elements of the collection
copy (col1.begin(), col1.end(), ostream_iterator(cout," "));
cout << endl;
// remove all elements with value 3
// - retain new end
list::iterator end = remove(col1.begin(), col1.end(), 3);
// print resulting elements of the collection
copy (col1.begin(), end, ostream_iterator(cout," "));
cout << endl;
// print number of resulting elements
cout << "number of removed elements: "
<< distance (end, col1.end()) << endl;
// remove "removed" elements
col1.erase (end, col1.end());
// print all elements of the modefied collection
copy (col1.begin(), col1.end(), ostream_iterator(cout," "));
cout << endl;
}
- distance()返回两个迭代器之间的距离。(若迭代器是随机存取迭代器,则可用operator-直接计算)
- erase()可删除“参数所指区间内”全部元素。
但通常无必要删除“已移除”的元素,只需将逻辑终点取代容器实际终点即可。
若需以单一语句删除元素,可以如此:col1.erase(remove(col1.begin(), col1.end(), 3), col1.end());
为什么算法不自己调用erase()?:
这个问题点出来STL为了获取灵活性而付出的代价。
透过“以迭代器为接口”,STL将数据结构和算法分离。
然而,迭代器只是“容器中某个位置”的抽象概念,
一般而言,迭代器对自己所属容器一无所知。
任何“以迭代器访问容器元素”的算法,都无法通过迭代器调用容器类别所提供的任何成员函数。
如此设计会导致:算法的操作对象不一定得是“容器内全部元素”形成的区间,
可以是元素的子集,甚至算法可运作于一个“未提供成员函数erase()”的容器(如array)。
故为使算法具有最大弹性,不要求“迭代器必须了解容器细节”是合理的。
5.6.2 manipulating algorithm and associative container(page115)
manipulating algorithm不能将关联式容器作为操作对象,因为:将manipulating algorithm作用于容器上会改变某位置的值,进而破坏关联式容器已序的特性,就推翻了关联式容器的基本原则(容器中元素总是据某排序准则自动排序)。
为了保证该准则,关联式容器的所有迭代器均被声明为指向常量。
从关联式容器中删除元素方法:调用成员函数。
每一中关联式容器都提供了移除元素的成员函数:
// stl/remove3.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
set col1;
// insert element from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.insert(i);
}
// print all elements of the collection
copy (col1.begin(), col1.end(), ostream_iterator(cout, " "));
cout << endl;
/* remove all elements with value 3
* - algorithm remove() does not work
* - instead member function erase() works
*/
int num = col1.erase(3);
// print number of removed elements
cout << "number of removed elements: " << num << endl;
// print all elements of the modified collection
copy (col1.begin(), col1.end(), ostream_iterator(cout," "));
cout << endl;
}
note:容器类提供了多个不同的erase()成员函数,其中一种形式是“以待删除的元素值”为唯一参数,返回被删除的元素个数。
5.6.3 算法VS成员函数(page116)
就算符合种种条件可以使用某算法(指algorithm中的函数),但未必是好的。容器本身可能提供功能类似但性能更好的成员函数。eg:对list元素调用remove()。
算法本身不知道工作于list上,故像在任何容器中一样做着相似的工作:
改变元素值,从而重新排列元素。
若移除第一个元素,则后面所有元素就会分别被赋值给各自的前一个元素。
这就违反了list的主要优点:通过修改link而非value来插入/移动/移除元素。
为了避免这个问题,list针对所有manipulating algorithm提供了一些对应的成员函数。
故,若使用list,则应使用这些成员函数。
且成员函数真的移除了“被移除”的元素(且不是某种移动而已)eg:
// stl/remove4.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
list col1;
// insert elements from 6 to 1 and 1 to 6
for (int i = 1; i <= 6; ++i)
{
col1.push_front(i);
col1.push_back(i);
}
// remove all elements with value 3
// - poor performance
col1.erase(remove(col1.begin(), col1.end(), 3), col1.end());
// remove all elements with value 4
// - good performance
col1.remove(4);
}
如果追求高效率,则应选择成员函数。(前提是知道某容器存在效率较好的成员函数)。
如果使用成员函数,一旦换用另一种容器,则不得不改动程序代码。
5.7 User-Defined Generic Functions(page118)
STL是一个可扩展的框架。故可自定义算法函数处理群集元素。当然这些操作函数也可以是泛型的(generic)。
为了在这些操作中声明有效的迭代器,须使用容器提供的型别,因为每种容器有各自的迭代器。
为了方便写出泛型函数,每种容器都提供了一些内部的型别定义。eg:
// 定义了一个泛型函数,可打印一个字符串(也可不指定),然后打印容器全部元素
// stl/print.hpp
#include
/* PRINT_ELEMENTS()
* - prints optional C-string optcstr followed by
* - all elements of the collection col1
* - separated by spaces
*/
template
inline void PRINT_ELEMENTS (const T& col1, const char* optcstr = "")
{
typename T::const_iterator pos;
std::cout << optcstr;
for (pos = col1.begin(); pos != col1.end(); ++pos)
{
std::cout << *pos << ' ';
}
std::cout << std::endl;
}
其中pos被声明为“传入容器型别”内的迭代器型别,typename用以表明const_iterator是型别T所定义的一个型别而非一个型别为T的值。
5.8 一函数作为算法函数的参数(page119)
一些算法函数可接受自定义的辅助性函数,以提高灵活性。
5.8.1函数作为算法函数的参数
eg:for_each(),针对区间内每个元素,调用一个用户自定义函数。
// stl/foreach1.cpp
#include
#include
#include
#include
using namespace std;
// function that prints the passed argument
void print (int elem)
{
cout << elem << ' ';
}
int main()
{
vector col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// print all elements
for_each (col1.begin(), col1.end(), print);
cout << endl;
}
输出:
算法函数对这些辅助函数的态度:有的视为是必要的,有的视为可有可无。可利用这些辅助函数来指定查找准则、排序规则或定义某种操作,以便将某种容器内的元素转换到另一个容器。eg:
// stl/transform1.cpp
#include
#include
#include
#include
#include
#include "print.hpp"
int square (int value)
{
return value*value;
}
int main()
{
std::set coll1;
std::vector coll2;
// insert elements from 1 to 9 into coll1
for (int i = 1; i <= 9; ++i)
{
coll1.insert(i);
}
PRINT_ELEMENTS(coll1,"initialized: ");
// transform each element from coll1 to coll2
// - square transformed values
std::transform (coll1.begin(), coll1.end(),
std::back_inserter(coll2),square);
PRINT_ELEMENTS(coll2,"squared: ");
}
输出:
5.8.2 predicate(谓词/判别式)(page121)
所谓predicates,即返回bool的函数。通常用于指定排序或查找准则。视情况可能有一或两个操作数。
note:并非任何返回bool的一元或二元函数就是合法的predicate,STL要求面对相同的值,predicates必须得出相同的结果,这个要求将“被调用时会改变自己内部状态”的函数清除出场。(详见page302)
- Unary Predicate(一元谓词/一元判别式)
Unary Predicate会检查唯一参数的某特性,eg:
// 用来搜寻第一个质数
// stl/prime1.cpp
#include
#include
#include
#include // for abs()
#include
using namespace std;
// predicate, which returns whether an integer is a prime number
bool isPrime (int number)
{
// ignore negative sign
number = abs(number);
// 0 and 1 are prime numbers
if (number == 0 || number == 1)
{
return true;
}
// find divisor that divides without a reminder
int divisor;
for (divisor = number/2; number%divisor != 0; --divisor)
{
;
}
// if no divisor greater than 1 is found,it is a prime number
return divisor == 1;
}
int main()
{
list col1;
// insert elements from 24 to 30
for (int i = 24; i <= 30; ++i)
{
col1.push_back(i);
}
// search for prime number
list::iterator pos;
pos = find_if (col1.begin(), col1.end(), isPrime);
if (pos != col1.end()) // found
{
cout << *pos << " is first prime number found " << endl;
}
else // not found
{
cout << "no prime number found " << endl;
}
}
输出:
find_if()在给定区间内寻找使“被传入的一元谓词”运算结果为true的第一个元素。如果没有一个元素使得一元谓词结果为true,find_if()就返回区间的终点即第二个参数。
- Binary Preidcate(二元谓词/二元判别式)(page123)
典型用途:比较两参数的特定属性。(eg定义operator<或自定义排序规则)eg:
// 根据每个人的姓名,对一组元素进行排序
// stl/sort1.cpp
#include
#include
#include
#include
#include
using namespace std;
class Person
{
public:
Person(const char* s1, const char* s2):ftname(s1),ltname(s2){}
string firstname() const
{
return string(ftname);
}
string lastname() const
{
return string(ltname);
}
//...
private:
const char* ftname;
const char* ltname;
};
/* binary function predicate:
* - returns whether a person is less than another person
*/
bool personSortCriterion (const Person& p1, const Person& p2)
{
/* a person is less than another person
* - if last name is less
* - if the last name is equal and the first name is less
*/
return p1.lastname() < p2.lastname() ||
( !(p2.lastname() < p1.lastname()) &&
p1.firstname() < p2.firstname());
}
int main()
{
deque col1;
col1.push_back(Person("Erii","Tom"));
col1.push_back(Person("Lise","Chris"));
// ...
sort (col1.begin(), col1.end(), personSortCriterion);
// ...
for (auto iter = col1.begin(); iter != col1.end(); ++iter)
{
cout << "firstname: " << iter->firstname() << "---lastname: " << iter->lastname() << endl;
}
}
输出:
注:也可用仿函数(functor/function object)来实现一个排序准则。如此的优点:实现的准则将是一个型别(type),可用于“声明一个set,以某型别为排序准则”之类的事。
5.9 仿函数(functor/function object)
传递给算法的“函数型参数(functional argument)”并不一定要是参数,可以是具有函数行为的对象,即function object/functor。函数行为指可以“使用小括号传递参数以此调用某个东西”。eg:
// 是page119例子的一个仿函数版本,行为和使用与一般函数完全相同
// stl/foreach2.cpp
#include
#include
#include
using namespace std;
// simple function object that prints the passed argument
class PrintInt
{
public:
void operator() (int elem) const
{
cout << elem << " ";
}
};
int main()
{
vector col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// print all elements
for_each (col1.begin(), col1.end(), PrintInt());
cout << endl;
}
表达式for_each(col1.begin(), col1.end(), PrintInt())中的PrintInt()产生此类的一个临时对象,作为for_each()的参数。for_each大致code:
namespace std
{
template
Operation for_each (Iterator act, Iterator end, Operation op)
{
while (act != end) // as long as not reached the end
{
op(*act); // call op() for actual element
++act; // move iterator to the next element
}
return op;
}
}
5.9.1 仿函数优点(page127)
相比较于一般函数,仿函数更复杂,但优点:
1. 仿函数是“smart functions”(智能型函数)
“行为类似指针”的对象,称为smart pointers,同理smart functions。
仿函数可拥有成员函数和成员变量,即仿函数拥有状态。
事实上,同一时间内,由某仿函数代表的单一函数可能有不同的状态(一般函数中不存此情况)。
另,可在执行期(指被调用之前)初始化它们。
2. 仿函数各有其型别
由仿函数定义的每个函数行为都有各自的型别。
故可将函数行为作为template参数运用。
3. 仿函数速度通常比一般函数快
就template而言,很多细节在编译期就确定了,故可能进行更好的优化。
故传入一个仿函数可能获得更好的性能。
- functor example(page127)
假设要对群集中各元素加上一个固定值。若编译期知晓该固定值,则可用一般函数:
void add10(int& elem)
{
elem += 10;
}
void f1()
{
vector col1;
// ...
for_each (col1.begin(), col1.end(), add10);
}
- 假设要对群集中各元素加上多个不同的固定值且编译期已知晓,则可使用template:
template
void add(int& elem)
{
elem += theValue;
}
void f1()
{
vector col1;
// ...
for_each (col1.begin(), col1.end(), add<10>);
}
使用仿函数,对象可以有自己的状态,可被正确的初始化。eg:
// stl/add1.cpp
#include
#include
#include
#include "print.hpp"
using namespace std;
// function object that adds the value with which it is initialized
class AddValue
{
private:
int theValue; // the Value to add
public:
// constructor initializes the values to add
AddValue(int v) : theValue(v){}
// the "function call" for the element adds the value
void operator() (int& elem) const
{
elem += theValue;
}
};
int main()
{
list col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
PRINT_ELEMENTS(col1, "initialized: ");
// add value 10 to each element
for_each (col1.begin(), col1.end(), AddValue(10));
PRINT_ELEMENTS(col1, "after adding 10: ");
// add value of first element to each element
for_each (col1.begin(), col1.end(), AddValue(*col1.begin()));
PRINT_ELEMENTS(col1, "after adding first element: ");
}
输出:
运用此法,前述“一个函数两个状态”问题可用“两个不同仿函数”解决。eg:声明两个仿函数让后各自运用。
AddValue addx(x); // function object that adds value x
AddValue addx(y); // function object that adds value y
for_each (col1.begin(), col1.end(), addx); // add value x to each element
// ...
for_each (col1.begin(), col1.end(), addx); // add value x to each element
// ...
for_each (col1.begin(), col1.end(), addx); // add value x to each element
同理可提供一些成员函数,在仿函数生命期间查改对象状态。
注:C++标准程序库未限制算法“对容器元素”调用仿函数的次数,故可能导致同一仿函数的若干副本传给元素。若将仿函数作为predicate用会导致很多麻烦。(详见p302)
5.9.2 预定义的仿函数(page131)
C++标准程序库包含了很多仿函数。eg:
operator<对应的排序准则仿函数是less<>,
故set col1; 会被扩展为set > col1; // sort elements with <
如此,若要方向排序,则set > col1; // sort elements with >
类似,将群集中元素取反:
transform (col1.begin(), col1.end(), // source
col1.begin(), // destination
negate() ); // operation
其中negate
同理,可对群集内元素取平方:
// process the square of all elements
transform (col1.begin(), col1.end(), // first source
col1.begin(), // second source
col1.begin(), // destination
multiplies() ); // operation
使用一些特殊的函数配接器(function adaptors),可将预定义的仿函数和其它数值组合在一起,或使用特殊情况。eg:
// stl/fo1.cpp
#include
#include
#include
#include
#include "print.hpp"
using namespace std;
int main()
{
set > coll1;
deque coll2;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
coll1.insert(i);
}
PRINT_ELEMENTS(coll1, "initialized: ");
// transform all element into coll2 by multiplying 10
transform (coll1.begin(), coll1.end(), // source
back_inserter(coll2), // destination
bind2nd(multiplies(), 10));// operation
PRINT_ELEMENTS(coll2, "transformed: ");
// replace value equal to 70 with 42
replace_if (coll2.begin(), coll2.end(), // range
bind2nd(equal_to(), 70), // replace criterion
42); // new value
PRINT_ELEMENTS(coll2, "replaced: ");
// remove all elements with value less than 50
coll2.erase(remove_if(coll2.begin(), coll2.end(), // range
bind2nd(less(), 50)), // remove criterion
coll2.end());
PRINT_ELEMENTS(coll2, "removed: ");
}
输出:
note:所有仿函数通常声明为inline。
另有些仿函数可调用群集内每个元素的成员函数:
for_each(col1.begin(), col1.end(), // range
mem_fun_ref(&Person::save) ); // operation
仿函数mem_fun_ref()用于调用其作用元素的某个成员函数。
5.10 容器内的元素(page134)
容器以特定方式操作其元素故容器元素须满足特定条件。
5.10.1 容器元素的条件
STL的容器、迭代器、算法都是template,故无论是预定义或自定义型别都可操作。但不同操作须满足不同条件,STL容器元素须满足三个基本条件:
1. 须通过copy构造函数进行复制。
所有容器都会在内部生成一个元素副本并返回该暂时性副本,故copy构造函数会被频繁调用。
若对象拷贝的功耗高,则用reference语意使用容器以避免拷贝。(详见page222)。
2. 须通过assignment操作符完成赋值。
容器和算法都用assignment才能以新元素改写旧元素。
3. 须通过析构函数完成销毁动作。
当容器元素被移除,其容器中的副本将被销毁,
故析构函数不能为private。
另,C++惯例,析构函数不允许抛出异常。
上述三个条件对任何class都是隐式成立的。(若没定义上述操作的特殊版本,也没定义“可能破坏这些操作健全性”的成员,则自然满足上述三条件)。
还应满足以下条件:
- 对sequence container,元素default构造函数须可用。
- 对某些操作须定义operator==以测试相等性。如有查询需求。
- associate container中,元素须定义排序准则。缺省是operator<(通过仿函数less<>被调用)。
5.10.2 Value语意 VS reference语意(page135)
STL只支持value语意而不支持reference语意。优缺点:
优点:
1. 元素的拷贝简单。
2. 使用reference易出错,
须确保reference所指对象存在,并小心偶现的循环引用。
缺点:
3. “拷贝元素”可能性能低下;有时无法拷贝。
4. 无法在多个不同容器中管理同一份对象。
想要获得适用于STL容器的reference语意,须自定义合适的智能指针(eg使用“引用计数”智能型指针实现STL容器的reference语意)。(详见page222)
5.11 STL内部错误处理和异常处理
逻辑性错误和运行环境所致的运行时错误都能被异常机制处理。
5.11.1 错误处理(page137)
STL设计原则是效率优先,安全次之,而错误检查耗时,故几乎没有。
大部分决定不加入错误检查,原因:
- 错误检查耗时会降低效率,而程序的总体目标是 速度。
- 若认为安全重于效率,则可加一层包装或使用STL特殊版本。但加入错误检验后就无法消除其以获得更高效率,反过来则可以。
故错误检验是可获得的但不是STL内在条件。
C++标准程序指出,对STL的任何运用,若违反规则会导致未定义行为(eg 索引、迭代器或区间不合法)。
具体而言,使用STL须满足以下要求:
1. 迭代器合法且有效。
迭代器可能某些操作会无效(vector/deque的插入删除、内存重配)
2. 迭代器指向“逾尾(past-the-end)”位置(end()和rend()),但不指向任何对象。
故不能对其调用oeprator*或operator->。
3. 区间须合法:
用以指出某钱的前后迭代器须指向同一容器。
从第一个迭代器出发可到达第二个迭代器所指位置。
4. 若区间不只一个,第二区间及后继区间拥有的元素须大于等于第一区间的元素数量。
5. overwrite操作的目标区间须有足够元素,否则就须用inert iterator。
eg,错误示例
// stl/iterbug1.cpp
#include
#include
#include
using namespace std;
int main()
{
vector coll1; // empty collection
vector coll2; // empty collection
/* RUNTIME ERROR:
* - beginning is behind the end of range
*/
vector::iterator pos = coll1.begin();
reverse (++pos, coll1.end());
// insert elements from 1 to 9 into coll2
for (int i = 1; i <= 9; ++i)
{
coll2.push_back(i);
}
/* RUNTIME ERROR:
* - overwriting nonexisting elements
*/
copy (coll2.begin(), coll2.end(), coll1.begin());
/* RUNTIME ERROR:
* - collections mistaken
* - begin() and end() mistaken
*/
copy (coll1.begin(), coll2.end(), coll1.end());
}
5.11.2 异常处理(page139)
C++标准程序库如今有如下保证(前提是析构函数不抛出异常):(异常发生时各种容器操作,详见page248)
1. 对所有“以节点为基础(node-based)实现”的容器(list/set/multiset/map/multimap etc):
若节点构造失败,容器保持不变不收影响(即支持事务回滚commit-or-rollback)。
2. 对"以array为基础(array-based)实现"的容器(vector/deque etc):
若插入元素失败,若想要完全回复,则须在插入操作之前拷贝插入点之后所有的元素。
push和pop作用与容器末端,不需拷贝元素即可回复原状。