王朝网络
分享
 
 
 

stl应用小问题

王朝other·作者佚名  2006-07-04
宽屏版  字体: |||超大  

1. 编译器的解析

list<int> data(istream_iterator<int>(cin),istream_iterator<int>());

这不是声明一个list变量 data,而是被认为是一格函数声明. 可以使用如下方法(effective stl 有讲)

istream_iterator<int> dataBeg(cin);

list<int> data(dataBeg,istream_iterator<int>());

当然还有

stack<int,list<int>> sk; >> 被解析为操作符.当然这个容易避免,中间加个空格就解决了stack<int,list<int> > sk;

2 . front() 与 begin()

一般经常使用容器的begin() 函数,因为常用 iterator 取得返回值,而front() 返回的是容器内第一个变量的引用.

vector<int> iv;

vector<int>::iterator it = v.begin();

vector<int>::reference ref = v.front();

对于一些api函数如fun(int *)需要的参数,做为输入参数 &*begin()写起来总是怪异。&front()相对好一点,当然&v[0] 更自然。特别是如需从第3个元素开始。&v[3] 更是最佳选择。

vc6 里面vector的begin()返回的是原类型(如vector<int> 是 int )。所以可以写 fun(begin())不会出错, stl 源码分析里面讲的 sgi stl 版本也是,但dev c++, vc2003 里面都明确改成了对内部类iterator的返回 。所以任何时候不要用fun(begin()),尽管有时候他能工作

3. 名称过长的警告

对于vc6使用stl(强烈建议换掉,巨多不标准,标准代码不过的地方,如list 的sort 不能指定排序比较函数 bool comp(参数1,参数2) 这样的函数) 才有这种现象,产生过多得c4786警告,影响编译速度,可以使用如下命令关闭此编译选项

#pragma warning(disable:4786)

4. 字符串

字符串长度变量要用 string::size_type 声明而不是int。基本每种容器都有这个typedef. 如需返回长度基本都是size_type类型。

判断是否到字符串结尾与 string::npos比较,跟'\0'比较时char* 字符的行为。

vc2003 的string并没有使用引用计数(dev里面使用了引用计数,好像vc6也用了), 这样 string str1 = str2; 的操作与char* 的 strcopy 效率没什么区别。

但对于 char* s = "123"; string str = s;这样的语句任何版本string 都不会使用引用计数,引用计数只有在同类之间赋值才存在

如 string s1 = "hello"; string s2 = s1;

如果使用引用计数 s1[1]='q'时系统要重新给s1分配内存。早分配还是晚分配区别并不明显? 假设str2没有使用,编译将其优化掉,那都是只分配一次内存。但没有考虑引用计数 s1[1]='q' 操作,就可以省掉对引用计数的判断。相反倒提升了速度。如果需要和s1相同指向的字符串,用引用就好了,string& s2=s1. 当然这种情况只是对字符串来说的

btw:

mfc 的CString 是采用引用计数的。c 字符串不像pascal 把字符串长度放在开头。但CString 序列化到文件,是字符长度在前面的。提前知道字符串长度有利于优化

还有 char* p = "hello world"; p 指向的是常量字符串, p[1] = '1', strcpy(p,"12345") 都是错误的,经常有新人问,这里提一下

5. map 插入元素,如果开始插入map中没有的元素使用insert 函数比用 map[]=这样赋值好一些,如果是hash_map这样先查找元素,找不到才执行插入. 如:

typedef map<string,string> m_type;

typedef m_type::value_type valType;

map< string, string > m;

string h = "hello";

string w = "world";

m.insert( valType(h,w));

6. do while(0)

相信有人看到下面这样的宏,其实这样就强制你必须在应用宏时加上结束符; 这样看起来才像是一个函数

#define xxx() do {xxx } while(0)

7. 初始化数组

int a[10];

memset(a,0,10*sizeof(int));

相信肯定有人这么做过(int a[]={0,0,0,0...} 这么干的肯定也有),其实不必这么麻烦 int a[10] = {0};这样就ok 了。如果不是初始化的时候,而且不是赋0 ,还是老老实实用 fill(a,a+10,1); (其实对于char数组他调用的还是memset,不存在效率问题)

当然对于vector 容器定义时便可指定初始值 vector<int> v(10,1);

8. 警惕函数参数

很多函数都是有偏特化版本的,对于特殊参数保证效率或有不同处理方法,这时要警惕输入参数。想使用特化版本,参数要保持一直。虽然好的编译器在release版本可能帮你选中特化版本。但最好不要太依赖于编译器。例如fill_n 版本

template<class _OutIt,class _Diff, class _Ty> inline

void fill_n(_OutIt _First, _Diff _Count, const _Ty& _Val){ ..... }

inline void fill_n(char *_First, size_t _Count, int _Val)

{

::memset(_First, _Val, _Count);

}

inline void fill_n(signed char *_First, size_t _Count, int _Val)

{

::memset(_First, _Val, _Count);

}

inline void fill_n(unsigned char *_First, size_t _Count, int _Val)

{

::memset(_First, _Val, _Count);

}

针对字符串选用memset进行赋值(能提升相当效率)。下面代码:

char str[300];

fill_n(str,150,97);

看似调用了fill_n(char*,size_t,int) (第二个函数), 其实调用的还是模板函数(第一个)。因为150 被编译器解释为int 类型,与fill_n(char*,size_t,int) 是不相符合的。还有 fill_n(str,(size_t)150,'a') 也没有调用优化版本,因为'a' 是char 而不是int 类型。

所以fill_n(str,150*sizeof(char),97) 这种写法才算完美。

主要注意有 size_t 参数的函数, 用size_t 声明或者多*个sizeof(type)

9. list 的 earse

对于vc2003 来说,判断了如果是end节点 则不删除。但sgi stl 没有判断。所以注意你在list 里面执行erase操作时是否会删掉这个节点。删掉的话整个链表的基石也就垮了。可以在dev 编译器(同gcc)里面执行下面程序,这个循环从begin() 开始都没得到链表节点。

int a[5] ={2,3,5,4,1};

list<int> lt(a,a+5);

lt.erase(lt.end()); //明显了点。

list<int>::iterator it;

for(it=lt.begin();it!=lt.end();it++)

{

cout<<*it<<" ";

}

或许不能删除end节点是使用者应该注意的。但看两个库的做法,即使牛人们也难以统一意见。个人意见是不用erase判断是否为头节点,但如果去面试的话。不介意你"画蛇添足"。

对于某些面试经常抓着这点不放,你的链表删除怎么没判断是否为头节点。你可以飞出一本stl源码剖析,砸在他头上,指着他的鼻子说: 你看人家C++标准库都可以这么做。

sgi stl 一般都没有对这些边界条件做判断,没人会使用list earse()去删end节点。同样如果没有确定front()是否有元素, 也不要在deque上执行pop_front操作。

10 deque 的空间

vc 中deque跟vector相似,自增长,但deque有多个大小相同的缓冲区,使用空间只会增长不会降低。如果想删除弹出节点空间应该使用list 。 而sgi stl 实现的deque (默认对于类型长度大于int)如果一个缓冲区没有数据就删除。对于pj stl 保证了速度,sgi stl 保证了空间。比较也没有太大意义。

11 set 的比较函数对象

给set 指定比较函数对象时,重载operator() 要声明成常量函数。如

class less_key{

public:

bool operator() (const foo &f1, const foo &f2) const

{

return (f1.key < f2.key);

}

};

因为set 取得比较函数对象类型后,把该类的常量类型传递给了底层的rb_tree,所以不声明operator()为常量函数,编译便会失败。而且这种关系比较函数对象并没有改类内成员,所以任何时候最好都声明为 const .

12 vector <vector<int> >

一般对于原始类型声明可变数组可以不指定长度如:

vector<int> iv;

iv.push_back(1);

但对于类型也是vector<vector<int> >

vector<vector<int> > ivv;

ivv[0].push_back(1);

cout<<ivv[0][0]<<endl;

当执行时。呵呵出错了。ivv[0] 还没有初始化就调用当然会出错。

vector<vector<int> > ivv;

ivv.push_back(vector<int>());

ivv[0].push_back(1);

cout<<ivv[0][0]<<endl;

如果先指定长度vector<vector<int> > ivv(10); 便可以初始化10个,毕竟使用2维数组,而且两个维度都可变的情况不多。这样在这10个之内就不需要调用ivv.push_back(vector<int>());

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有