使用stl 注意细节
由于这些细节是从别的地方摘入和个人平时遇到的问题,如果有什么错误请及时指正。
1、auto_ptr 不能用new[]所生成的array作为初值,因为释放内存时用的是delete,而不是delete[]c
2、迭代器使用过程中优先选用前置式递增操作符(++iter)而不是选择后置式递增操作符(iter++)。
3、迭代器三个辅助函数:advance(),distance(),iter_swap()。
advance()可令迭代器前进
distance()可处理迭代器之间的距离。
iter_swap()可交换两个迭代器所指内容。
4、heap函数 makeheap()、push_heap()、pop_heap()、sort_heap()
5、’/0’在string之中并不具有特殊意义,但是在一般C形式的string中却用来标记字符串结束。在string中,字符 ‘/0’和其他字符的地位完全相同。string中有三个函数可以将字符串内容转换成字符数组或C形式的string。
data() 以字符数组的形式返回字符串内容。但末未追加’/0’字符,返回类型并非有效的C形式string。
c_str() 以C形式返回字符串内容(在末尾端添加’/0’字符)。
copy() 将字符串内容复制到“调用者提供的字符数组”中,不添加’/0’字符。
6、容器中用empty来代替检查size是否为0;当使用new得到指针的容器时,切记在容器销毁前delete那些指针;千万不要把auto_ptr放入容器中。
7、尽量使用vector和string来代替动态申请的数组;避免使用vector<bool>,vector<bool>有两个问题.第一,它不是一个真正STL容器,第二,它并不保存bool类型。
8、迭代器使用过程中,尽量使用iterator代替const_iterator,reverse_iterator和const_reverse_iterator;使用distance和advance把const_iterators转化成iterators。
typedef deque<int> IntDeque; // 和以前一样
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
IntDeque d;
ConstIter ci;
... // 让ci指向d
Iter i(d.begin()); // 初始化i为d.begin()
advance(i, distance(i, ci)); // 调整i,指向ci位置
9、避免对set和multiset的键值进行修改。
10、永远让比较函数对相同元素返回false。下面的程序是我最近在看数据结构时发现的,这个版本堪称完美,我还是很认同的,具体情况我就不做说明了,因为前面还有两个版本了,所以就无法展开细说了。
//前提:有序的容器
//对于这段程序,我没有对输入参数的做有效的校验,请各位注意使用
//这程序的语义定义是,如果找到e 则返回对于的位置,否则返回,比e小的最大元素的位置,
Long bsearch(long arr[],long e,long lo,long lh)
{
Long lm(0);
While(lo < lh)
{
lm = (lo + lh) >> 1;
e < arr[lm] ? lh = lm : lo = lm + 1; //把=e 划分到右边,这样做的好处是为,我们自己更便于理解,当然stl中也是遵循这个规则的。
}
Return --lo;
}
11、排序选择:
1)如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
2)如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
3)如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。
4)如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。
5)如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务。
12、如果你真的想删除东西的话就在类似remove的算法后接上erase。remove从一个容器中remove元素不会改变容器中元素的个数,erase是真正删除东西。
13、提防在指针的容器上使用类似remove的算法,在调用类似remove的算法前手动删除和废弃指针。
14、尽量用成员函数代替同名的算法,有些容器拥有和STL算法同名的成员函数。关联容器提供了count、find、lower_bound、upper_bound和equal_range,而list提供了remove、remove_if、unique、sort、merge和reverse。大多数情况下,你应该用成员函数代替算法。这样做有两个理由。首先,成员函数更快。其次,比起算法来,它们与容器结合得更好(尤其是关联容器)。那是因为同名的算法和成员函数通常并不是是一样的。
15、容器中使用自定义的结构体时,如果用到拷贝与赋值,结构体需要重载operator=符号;比较容器分成相等与不等,相等时重载operator==符号,不等时重载operator<符号。比如set、map、multiset、multimap、priority_queue等容器类要求重载operator<符号。
16、Map/Multimap,Sets/Multisets都不能用push_back,push_front,因为它是自动排序的。
Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素。
Map内的相同数值的元素只能出现一次,Multimap内可包含多个数值相同的元素。内部由二叉树实现,便于查找。
17、string 与 数字之间的转换,转换的方法有很多种,一般使用stringstream来实现转换。比如:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
int i=0;
string temp;
stringstream s;
//string转换为数字
temp = “1234”;
s<<temp;
s>>i;
cout<<i<<endl;
//数字转换为string
i=256;
s<<i;
temp = s.str();
cout<<temp<<end;
system("pause");
return 0;
}
18、对于自定义的结构体,放入容器中,最好不要对容器进行内存初始化(不要调用memset,zeromemory函数),否则如果结构体中有指针类型的变量时,就会出现问题。这个问题,我相信有很多人都犯过吧,当然我就是其中一位。
19、Vector的函数泄漏问题
定义了一个
struct temp
{
char name[256];
int i;
}
Vector<temp> vect;
当对这个vect执行pushback一些temp的结构体后,执行clear这样是否会内存泄露?可以释放掉temp结构体中的name内存吗?
解决方法:
不行,clear只是把那些元素全部删除掉,并不是释放内存。再者,你这样的定义容器是不需要释放内存的,如果你这样定义,std::vector <temp> *pVec。就需要了。先pVec->clear()再 pVec->swap( (std::vector <temp>)(*pVec) )。就能实现内存的释放。
20、stl之map erase方法的正确使用
STL的map表里有一个erase方法用来从一个map中删除掉指令的一个节点,不存在任何问题。
如果删除多一个节点时,需要使用正确的调用方法。比如下面的方法是有问题:
for(ITER iter=mapTest.begin();iter!=mapTest.end();++iter)
{
cout<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter);
}
这是一种错误的写法,会导致程序行为不可知.究其原因是map 是关联容器,对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。
正确的使用方法:
1).使用删除之前的迭代器定位下一个元素。STL建议的使用方式
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter++);
}
或者
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
ITER iterTmp = iter;
iter++;
cout<<iterTmp->first<<":"<<iterTmp->second<<endl;
mapTest.erase(iterTmp);
}
2). erase() 成员函数返回下一个元素的迭代器
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout<<iter->first<<":"<<iter->second<<endl;
iter=mapTest.erase(iter);
}
21、std::bind(c++11)
bind 是一组重载的函数模板.用来向一个函数(或函数对象)绑定某些参数. bind的返回值是一个函数对象.
性质:
不是函数,是一个class,是一个多元仿函数
模板参数:
带模板参数,但不需要,会自动推导!
构造函数参数:
格式:_需要绑定类型,_参数1,_参数2,_参数3,_参数4…
_需要绑定类型:可以是普通函数,类成员函数,成员变量
_参数N:可以是一个占位符,或者实际参数。
如果绑定的类型是一个类成员函数或变量,那么第一个参数必须是对象或者对象指针。
仿函数参数:
任意
仿函数返回值
如果绑定的是函数,返回绑定函数的返回值。
如果绑定是成员变量,返回成员变量值
占位符:
_1,_2,_3,_4….._9
占位符的数字表示仿函数时对应参数的位置。
一个bind里可以嵌入多个bind,但占位符是相对于这一块的bind是共享。
注意事项
a)如果绑定的是类函数,传入对象时,最好使用对象指针,如果使用对象实例会产生多次对象复制。如果非要传对象而不想多次被复制传在在使用ref或cref(ref的const版)
b) 跟lambda混用时一定要特别小心
第一、 会与lambda的占位符有冲突
第二、 lambda库里有跟同样名字的bind,功能类似,但没有此功能强大
总结
无模板参数,构函数对绑定函数负责,仿函数是任意的。
举例说明
例一:
void nine_arguments(
int i1,int i2,int i3,int i4,
int i5,int i6,int i7,int i8, int i9) {
std::cout << i1 << i2 << i3 << i4 << i5
<< i6 << i7 << i8 << i9 << '/n';
}
int main() {
int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
(boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7))
(i1,i2,i3,i4,i5,i6,i7,i8,i9);
}
输出结果921638457
23、平时我们用的最多的是 二元函数对象
template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
如果为了更方便的使用,以后的仿函数(函数对象)最好都继承binary_function
template <class T> struct less_equal : binary_function <T,T,bool> { bool operator() (const T& x, const T& y) const {return x<=y;}};
关于谓语函数对象都继承binary_function
Eg:Bind_1st和 Bind_2nd 这两用于捆绑第一个参数和第二个参数