第30条:确保目标区间足够大
template < class InputIt , class OutputIt , class UnaryOperation >
OutputIt transform ( InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op ) {
while ( first1 != last1) {
* d_first++ = unary_op ( * first1++ ) ;
}
return d_first;
}
int f ( int x) { return x* x; }
vector< int > v{ 1 , 2 , 3 , 4 , 5 } ;
vector< int > res;
transform ( v. begin ( ) , v. end ( ) , res. end ( ) , f) ;
transform ( v. begin ( ) , v. end ( ) , back_inserter ( res) , f) ;
res. resize ( v. size ( ) ) ;
transform ( v. begin ( ) , v. end ( ) , res. begin ( ) , f) ;
对于需指定一个目标区间的算法,要保证目标区间足够大,才能在向其写值的时候有效
要么提前分配足够的空间,目标区间保证足够大,才能向有效的位置写入值而不是妄图向一个容器以外的位置赋值 要么使用插入型迭代器,保证目标区间随着算法的运行而增大(如ostream_iterator,或由Inserter,back_inserter,front_inserter返回的迭代器);其中back_inserter只适用于提供了push_back的容器vector,string,deque,list; front_inserter只适用于提供了push_front的容器deque,list;
第31条:了解各种与排序有关的选择
sort, 及稳定版本stable_sort 注:list有专门的版本list:: sort , forward_list有专门版本forward_list:: sort
partial_sort
nth_element
partition,及稳定版本stable_partition
vector,string,deque,array:随机访问迭代器
list:双向迭代器
forward_list:单向迭代器
有序的关联容器:双向迭代器
stack, queue, priority_queue:不支持迭代器,都是容器适配器
第32条:如果确实需要删除元素,则需要在remove这一类算法之后再调用容器的erase成员函数
STL中实现删除操作的算法:remove,remove_if,unique STL的算法无法真正删除元素,所作的只是调整元素的位置 remove的实例:
vector< int > v{ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ;
v[ 3 ] = v[ 5 ] = v[ 9 ] = 99 ;
vector< int > :: iterator it = remove ( v. begin ( ) , v. end ( ) , 99 ) ;
cout << v. size ( ) << ' ' ;
for ( auto c : v) {
cout << c << ' ' ;
}
cout << endl;
v. erase ( it, v. end ( ) ) ;
cout << v. size ( ) << ' ' ;
for ( auto c : v) {
cout << c << ' ' ;
}
cout << endl;
STL中的list比较特殊,list::remove, list::remove_if, list::unique都是真正删除了容器元素的成员函数函数,并且比其他容器使用的remove-erase, remove_if-erase, unique-erase更高效:
std:: list< int > l = { 1 , 100 , 2 , 2 , 3 , 10 , 1 , 1 , 11 , - 1 , - 1 , - 1 , 12 } ;
cout << l. size ( ) << endl;
l. remove ( 1 ) ;
cout << l. size ( ) << endl;
l. remove_if ( [ ] ( int n) { return n > 10 ; } ) ;
cout << l. size ( ) << endl;
l. unique ( ) ;
cout << l. size ( ) << endl;
除了list,其它顺序容器想真正删除容器元素需结合remove-erase, remove_if-erase, unique-erase;关联容器直接使用erase成员函数;
第33条:对包含指针的容器使用remove这一类算法要特别小心
对于存放指向new分配的内存的指针的容器,若要对这种容器使用remove,remove_if,unique等类似的算法,为了防止资源泄露,措施有:
容器中存放带引用计数的智能指针,如shared_ptr; 由于容器中每个shared_ptr元素均指向各自动态分配的内存块,即引用计数均为1,当使用remove类的算法时,每当“删除”一个元素时,即这个智能指针会指向别处导致引用计数降为0,因此shared_ptr类会自动释放所指向的内存块; 笨办法:在对容器调用remove等算法之前,先手动析构那些将要被“删除”的指针所指向的对象并释放其内存;
第34条:了解那些算法要求使用排序的区间作为参数
很多算法需要提供迭代器范围,不同算法对于区间的要求不同:
若算法要求提供排序区间参数,就必须传给它一个排序的区间 算法所需要的比较函数,与排序所使用的比较函数必须一致
binary_search默认需要的区间是升序的,则必须提供一个升序区间范围给它,即算法需要什么区间就得给什么
ForwardIt lower_bound ( ForwardIt first, ForwardIt last, const T& value) ;
ForwardIt upper_bound ( ForwardIt first, ForwardIt last, const T& value ) ;
bool binary_search ( ForwardIt first, ForwardIt last, const T& value ) ;
std:: pair< ForwardIt, ForwardIt> equal_range ( ForwardIt first, ForwardIt last, const T& value ) ;
OutputIt set_union ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first ) ;
OutputIt set_intersection ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first ) ;
OutputIt set_difference ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first) ;
OutputIt set_symmetric_difference ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first) ;
OutputIt merge ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first ) ;
void inplace_merge ( BidirIt first, BidirIt middle, BidirIt last ) ;
bool includes ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2 ) ;
第35条:通过mismatch或lexicographical_compare实现简单的忽略大小写的字符串比较
借助两种算法mismatch或lexicographical_compare,实现可以忽略英文字母大小写的字符串比较函数; 1.通过mismatch实现:
template < class InputIt1 , class InputIt2 , class BinaryPredicate >
std:: pair< InputIt1, InputIt2>
mismatch ( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p)
{
while ( first1 != last1 && first2 != last2 && p ( * first1, * first2) ) {
++ first1, ++ first2;
}
return std:: make_pair ( first1, first2) ;
}
template < class InputIt1 , class InputIt2 , class BinaryPredicate >
std:: pair< InputIt1, InputIt2>
mismatch ( InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p)
{
while ( first1 != last1 && p ( * first1, * first2) ) {
++ first1, ++ first2;
}
return std:: make_pair ( first1, first2) ;
}
int ciCharCompare ( char c1, char c2) {
int lc1 = tolower ( static_cast < unsigned char > ( c1) ) ;
int lc2 = tolower ( static_cast < unsigned char > ( c2) ) ;
if ( lc1 > lc2) return 1 ;
else if ( lc1 < lc2) return - 1 ;
else return 0 ;
}
int ciStringCompareImpl ( const string& s1, const string& s2) {
typedef pair< string:: const_iterator, string:: const_iterator> PSCI;
PSCI p = mismatch ( s1. begin ( ) , s1. end ( ) , s2. begin ( ) , not2 ( ptr_fun ( ciCharCompare) ) ) ;
if ( p. first == s1. end ( ) ) {
if ( p. second == s2. end ( ) ) return 0 ;
else - 1 ;
}
else {
return ciCharCompare ( * p. first, * p. second) ;
}
}
int ciStringCompare ( const string& s1, const string& s2) {
if ( s1. size ( ) > s2. size ( ) ) return - ciStringCompareImpl ( s2, s1) ;
else return ciStringCompareImpl ( s1, s2) ;
}
2.通过lexicographical_compare实现:
template < class InputIt1 , class InputIt2 , class Compare >
bool lexicographical_compare ( InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
Compare comp)
{
for ( ; ( first1 != last1) && ( first2 != last2) ; ++ first1, ( void ) ++ first2 ) {
if ( comp ( * first1, * first2) ) return true ;
if ( comp ( * first2, * first1) ) return false ;
}
return ( first1 == last1) && ( first2 != last2) ;
}
bool ciCharLess ( char c1, char c2) {
return tolower ( static_cast < unsigned char > ( c1) ) < tolower ( static_cast < unsigned char > ( c2) ) ;
}
bool ciStringCompare ( const string& s1, const string& s2) {
return lexicographical_compare ( s1. begin ( ) , s1. end ( ) , s2. begin ( ) , s2. end ( ) , ciCharLess) ;
}
3.通过调用非标准c库的函数stricmp或strcmpi实现,实际未提供此函数;
#include <string.h>
extern int stricmp ( char * s1, char * s2) ;
当s1= s2时,返回值= 0
当s1< s2时,返回值< 0 , 但不一定是- 1
当s1> s2时,返回值> 0 ,但不一定是1
int ciStringCompare ( const string& s1, const string& s2) {
return stricmp ( s1. c_str ( ) , s2. c_str ( ) ) ;
}
best practice: 前两种算法实现使用的是标准STL算法,但速度不如第三种; stricmp或strcmpi速度快;但是非标准c库的函数,因此可移植性不强;传入的参数是char*而不是string;
第36条:理解copy_if算法的正确实现
bool isDefective ( const Widget& w) ;
struct isDefective{
bool operator ( ) ( const Widget& w) {
. . .
}
} ;
template < class InputIt , class OutputIt , typename UnaryPredicate>
OutputIt copy_if ( InputIt begin, InputIt end, OutputIt d_first, UnaryPredicate p) {
return remove_copy_if ( begin, end, d_first, not1 ( ptr_fun ( p) ) ) ;
}
template < class InputIt , class OutputIt , typename UnaryPredicate>
OutputIt copy_if ( InputIt begin, InputIt end, OutputIt d_first, UnaryPredicate p) {
while ( begin != end) {
if ( p ( * begin) ) * d_first++ = * first++ ;
}
}
第37条:使用accumulate或者for_each进行区间统计
accumulate的实现: 注:传入的第三参数表示累计的初始值,最后得到的类型也是T(因此可以直接得到最终的平均值点),因此往往T和迭代器区间的元素类型相同; 第三参数在遍历迭代器返回这个过程中是重复用的(注意体会这点); (若指定的话)第三参数的谓词是二元的
template < class InputIt , class T >
T accumulate ( InputIt first, InputIt last, T init)
{
for ( ; first != last; ++ first) {
init = std:: move ( init) + * first;
}
return init;
}
template < class InputIt , class T , class BinaryOperation >
T accumulate ( InputIt first, InputIt last, T init, BinaryOperation op)
{
for ( ; first != last; ++ first) {
init = op ( std:: move ( init) , * first) ;
}
return init;
}
for_each的实现: 注:谓词f是一元的;且返回的结果是一个函数对象
template < class InputIt , class UnaryFunction >
constexpr UnaryFunction for_each ( InputIt first, InputIt last, UnaryFunction f)
{
for ( ; first != last; ++ first) {
f ( * first) ;
}
return f;
}
list< double > ld;
. . .
double sum = accumulate ( ld. begin ( ) , ld. end ( ) , 0.0 ) ;
例2:求容器中string元素的长度和 对于标准容器,container::size_type 等价于 size_t
string:: size_type stringLengthSum ( string:: size_type sumSoFar, const string& s) {
return sumSoFar + s. size ( ) ;
}
set< string> ss;
. . .
string:: size_type lengthSum = accumulate ( ss. begin ( ) , ss. end ( ) , static_cast < string:: size_type> ( 0 ) , stringLengthSum) ;
例3:计算区间中数值的乘积(有直接用的函数子类模板multiplies)
vector< float > vf;
. . .
float res = accumulate ( vf. begin ( ) , vf. end ( ) , 1.0f , multiplies< float > ( ) ) ;
struct point{
point ( double initX, double initY) : x ( initX) , y ( initY) { }
double x, y;
} ;
方法1 : 使用accumulate
class PointAvg : public binary_function< point, point, point> {
private :
size_t numPoints;
double sumX;
double sumY;
public :
PointAvg ( ) : sumX ( 0 ) , sumY ( 0 ) , numPoints ( 0 ) { }
const point operator ( ) ( const point& a, const point& b) {
++ numPoints;
sumX + = b. x;
sumY + = b. y;
return point ( sumX / numPoints, sumY / numPoints) ;
}
} ;
list< ponit> lp;
. . .
point avg = accumulate ( lp. begin ( ) , lp. end ( ) , point ( 0.0 , 0.0 ) , PointAvg ( ) ) ;
方法2 :使用for_each
class PointAvg : public unary_function< > {
private :
size_t numPoints;
double sumX;
double sumY;
public :
PointAvg ( ) : sumX ( 0 ) , sumY ( 0 ) , numPoints ( 0 ) { }
void operator ( ) ( const point& a) {
numPoints++ ;
sumX + = a. x;
sumY + = a. y;
}
point result ( ) const {
return point ( sumX / numPoints, sumY / numPoints) ;
}
} ;
list< ponit> lp;
. . .
point avg = for_each ( lp. begin ( ) , lp. end ( ) , PointAvg ( ) . result ( ) ) ;
总结:实际上这两种方法区别就在于算法实现的不同:accumulate和for_each接受的谓词一个一元一个二元,且返回类型也不同;