变动型算法:manipulating algorithm
1. remove移除元素
不改变群集中的元素数目,end()返回的还是原来的那个点,size()还是当初那个大小,元素次序改变,有些元素被删了,删掉的元素的位置由后面的元素替换。一般可以erase掉remove()返回的iterator到end()之间的元素来真正实现删除。
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
int main()
{
list<int> coll;
for(int i=1; i<=6; ++i)
{
coll.push_front(i);
coll.push_back(i);
}
list<int> coll1(coll.begin(), coll.end());
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," ")); //6 5 4 3 2 1 1 2 3 4 5 6
cout << endl;
remove(coll.begin(), coll.end(), 3);
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," ")); //6 5 4 2 1 1 2 4 5 6 5 6
cout << endl;
copy(coll1.begin(), coll1.end(), ostream_iterator<int>(cout," ")); //6 5 4 3 2 1 1 2 3 4 5 6
cout << endl;
list<int>::iterator end = remove(coll1.begin(), coll1.end(), 3);
cout << "number of removed elements : " << distance(end, coll1.end()) << endl; //2
coll1.erase(end, coll1.end());
copy(coll1.begin(), coll1.end(), ostream_iterator<int>(cout," ")); //6 5 4 2 1 1 2 4 5 6
cout << endl;
return 0;
}
remove():返回逻辑上的新终点
distance():返回两个迭代器之间的距离
erase():删除迭代器之间的元素
不直接调用容器的算法:这个设计使得算法的操作对象不一定非得是整个容器内的所有元素,可以是那些元素的子集。
2. 与关联式容器 -- 用于它会出问题
因为关联式容器总是根据自身某个排序准则自动排序,所以关联式容器的所有迭代器均被声明为指向常量
删除元素:erase()函数
#include <iostream>
#include <set>
#include <iterator>
#include <algorithm>
using namespace std;
int main()
{
set<int> coll;
for(int i=1; i<=9; ++i)
{
coll.insert(i);
}
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));//1 2 3 4 5 6 7 8 9
cout << endl;
int num = coll.erase(3);
cout << "number of removed elements : " << num << endl; //1
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));//1 2 4 5 6 7 8 9
cout << endl;
return 0;
}
如果是list,使用成员函数也许是个更好的选择,效率更高:
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
using namespace std;
int main()
{
list<int> coll;
for(int i=1; i<=9; ++i)
{
coll.push_front(i);
coll.push_back(i);
}
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));
//9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9
cout << endl;
coll.remove(3);
copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));
//9 8 7 6 5 4 2 1 1 2 4 5 6 7 8 9
cout << endl;
return 0;
}
使用自定义泛型函数:
//print.cpp
#include <iostream>
using namespace std;
template <class T>
inline void print_elements(const T& coll, const char* optstr="")
{
typename T::const_iterator pos;
cout << optstr;
for(pos=coll.begin(); pos!=coll.end(); ++pos)
{
cout << *pos << " ";
}
cout << endl;
}
//testPrint.cpp
#include <iostream>
#include <list>
#include "print.cpp"
using namespace std;
int main()
{
list<int> coll;
for(int i=1; i<=9; ++i)
{
coll.push_back(i);
}
print_elements(coll, "list : ");
return 0;
}
//list : 1 2 3 4 5 6 7 8 9
注意:typename不可少,表明const_iterator是性别T所定义的一个型别,而不是一个型别为T的值
以函数作为算法的参数:被算法用作参数内部调用
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void print(int elem)
{
cout << elem << " ";
}
int main()
{
vector<int> coll;
for(int i=1; i<=9; ++i)
{
coll.push_back(i);
}
for_each(coll.begin(), coll.end(), print);
cout << endl;
return 0;
}
//1 2 3 4 5 6 7 8 9
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
#include <iterator>
#include "print.cpp"
using namespace std;
int square(int value)
{
return value * value;
}
int main()
{
set<int> c1;
vector<int> c2;
for(int i=1; i<=5; ++i)
{
c1.insert(i);
}
print_elements(c1, "init c1: ");//init c1: 1 2 3 4 5
std::transform(c1.begin(), c1.end(), std::back_inserter(c2), square);
print_elements(c2, "squared : ");//squared : 1 4 9 16 25
return 0;
}
判断式:一种函数,返回布尔值,通常用来指定排序准则和搜索准则
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
bool isPrime(int num)
{
if(num == 1 || num == 0)
return false;
if(num == 2)
return true;
for(int i=2; i <= sqrt(num); i++)
{
if(num % i == 0)
return false;
}
return true;
}
int main()
{
vector<int> c;
for(int i=24; i<=30; ++i)
{
c.push_back(i);
}
vector<int>::iterator iter;
iter = find_if(c.begin(), c.end(), isPrime);
if(iter != c.end())
{
cout << *iter << " is first prime number" << endl;//29 is first prime number
} else
{
cout << "no prime number" << endl;
}
return 0;
}
find_if函数在给定区间寻找使得被传入的isPrime判断式结果为true的第一个元素
函数对象:function object
类似函数的对象,泛型编程的抽象概念
定义:
class X {
public:
return-value operator()(arguments) const
{
//...
}
};
X fo;
fo(arg1, arg2); //等价于call fo.operator()(arg1,arg2);
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class PrintInt
{
public:
void operator() (int elem) const
{
cout << elem << " ";
}
};
int main()
{
vector<int> coll;
for(int i=1; i<=5; i++)
{
coll.push_back(i);
}
for_each(coll.begin(), coll.end(), PrintInt());
cout << endl;
return 0;
}
//1 2 3 4 5
PrintInt()会产生一个临时对象
foreach的第三个参数:如果是个一般函数,就以参数调用之,如果是个函数对象,就以参数调用函数对的operator()运算符
比起一般函数,函数对象具有以下优点:
1. 智能型函数,因为可以拥有成员函数和成员变量,还可以在执行期间初始化它们
2. 每个函数对象都有自己的类型,利用template,可以将函数行为当作template参数使用
3. 函数对象比一般函数速度快,template更多细节在编译期间就确定。
现在有个需求,对容器里的每个元素加上一个值
如果在编译期间就知道这个固定值,则可以这样:
void add10(int& elem)
{
elem = elem + 10;
}
void f()
{
vector<int> coll;
for_each(coll.begin(), coll.end(), add10);
}
如果需要多个不同的值,编译期间已经确定:
template <int theValue>
void add(int& elem)
{
elem = elem + theValue;
}
void f()
{
vector<int> coll;
for_each(coll.begin(), coll.end(), add<10>);
}
使用函数对象,则更灵活:
#include <iostream>
#include <list>
#include <algorithm>
#include "print.cpp"
using namespace std;
class AddValue
{
private:
int theValue;
public:
AddValue(int v) : theValue(v) {}
void operator() (int& elem) const
{
elem = elem + theValue;
}
};
int main()
{
list<int> c;
for(int i=1; i<=5; ++i)
{
c.push_back(i);
}
print_elements(c, "initialized : ");//1 2 3 4 5
for_each(c.begin(), c.end(), AddValue(10));
print_elements(c, "after add 10 : ");//11 12 13 14 15
for_each(c.begin(), c.end(), AddValue(*c.begin()));
print_elements(c, "after add first elem : ");//22 23 24 25 26
AddValue add100(100);
for_each(c.begin(), c.end(), add100);
print_elements(c, "after add 100 : "); //122 123 124 125 126
return 0;
}
C++ STL预先定义的函数对象:
1. 作为排序准则的函数对象,operator < 缺省排序准则是less<>从小到大
set<int> c; //其实就是set<int, less<int> > c;
set<int, greater<int> > c; //从大到小
2. 相反值:negate<int>()
transform(c.begin(), c.end(), c.begin(), negate<int>());
函数配接器:将预定义的仿函数和其他数值结合在一起
#include "print.cpp"
#include <set>
#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
set<int, greater<int> > c1;
deque<int> c2;
for(int i=1; i<=5; ++i)
{
c1.insert(i);
}
print_elements(c1, "set : "); //5 4 3 2 1
transform(c1.begin(), c1.end(), back_inserter(c2), bind2nd(multiplies<int>(), 10));
print_elements(c2, "transformed : "); //50 40 30 20 10
replace_if(c2.begin(), c2.end(), bind2nd(equal_to<int>(), 30), 55);
print_elements(c2, "replaced : "); //50 40 55 20 10
c2.erase(remove_if(c2.begin(),c2.end(),bind2nd(less<int>(),50)), c2.end());
print_elements(c2, "removed : "); //50 55
return 0;
}
容器内元素的条件:
1. 可透过copy构造函数进行复制。副本与原本必须相等。
2. 透过assignment操作符完成赋值操作。
3. 透过析构函数完成销毁动作。析构函数不能为private
4. 对于序列式容器,元素的默认构造函数可用
5. 对于某些操作,必须定义 == 执行相等操作
6. 关联式容器元素必须定义排序准则,缺省下operator<,透过仿函数less<>调用
STL容器提供的是value语意,所容纳的副本而不是对象本身,而不支持reference。
好处:拷贝简单;reference容器出错;
缺点:拷贝性能不好;无法在不同的容器中管理同一份对象;
但我们可以使用指针作为容器元素,必须很谨慎使用!
千万不要在容器内放置auto_ptrs,拷贝和赋值操作后,目标对象与原来对象不相等。
通过引用计数智能型指针,可以实现STL容器的reference语意
STL内部错误处理和异常处理:
错误处理:
STL的任何运用,如果违反规则,将会导致未定义行为
迭代器务必合法且有效
一个迭代器如果指向pass-the-end位置,并不指向任何对象
区间必须合法 - 前后两个迭代器必须指向同一个容器;从第一个出发能够到达第二个
如果涉及多个区间,第二区间必须拥有至少第一区间一样多的元素
覆盖动作中目标区间必须拥有足够的元素,否则用insert iterator