一, 定义
STL = Standard Template Library,标准模板库
http://baike.baidu.com/link?url=ZgViQn8lnJ3Z8MhF-R8MmEeiUeXUi9cnrCXCxUhC0PioEpdOQQ4FbNBOUOLpN6YhNLxEX2OSJPIfVOOf8ekaZ_yiA77k6mn8pFpgc2MvPwC
STL容器就为我们提供了这样的方便,它允许我们重复利用已有的实现构造自己的特定类型下的数据结构,通过设置一些模板类,STL容器对最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,可以将我们许多重复而乏味的工作简化。
二, 为何某些公司不允许使用C++ STL?
听朋友说他们公司不允许使用 C++ STL。有点不懂,为何不允许使用呢?难道出于安全、效率?不应该啊,当时急着没细问,大家能解释一下吗?不知道 Boost 如何?
说几个STL的缺点吧,虽然都是在比较极端的情况下出现,但是对于一些大项目还是会遇到的
1. 代码膨胀问题
每一个实例化过的模板类,都会膨胀出一份独立的代码,比如
std::vector
2. 内存使用效率问题 (以vc++2008为例)
stl在内存使用效率上是比较低效的,比如std::string,它的sizeof大概是28,因为它有一个内置的16字节数组,用来做小字符串优化的,就是说低于16字节的字符串都会至少占用28字节内存,如果刚好17字节字符串,则会占用28字节+额外分配的字符串内存,额外分配的内存是一个堆块,又有很多浪费,相比用一个char *存储字符串大约多占用了一倍内存。
还有map<>,每一个map的node都是一块独立分配的内存,如果是 map
如果元素数量有百万级,那么内存占用就很可观了,这种情况下建议自己实现allocator,做内存池。
3. deep copy问题
让两个容器的实例做赋值操作,看起来就一条语句,实际上容器里的每个元素都执行了一次赋值操作。如果容器里有百万级的数据,那么一个等号就产生了几百万次的构造和析构。
传递参数的时候一定要用 const 引用,赋值可以用 swap代替。
4. 隐式类型转换
比如 有个函数
void doSomething(const std::string &str);
调用的时候
doSomething("hello");
能编译执行,但是会产生一个临时的匿名的std::string实例,把"hello"复制一遍,然后在调用完成后析构掉。如果这个发生在循环体内部有可能影响性能。
以上这些问题,在小程序里或者数据规模不大的时候,比如容器内元素只有几千这个规模,都不是什么大问题,那时开发效率才是重点,但是一旦有大数据stl容器会成为性能瓶颈的。
我并不是主张不用STL,而是要充分了解STL的优缺点,根据应用场景做选择。
1,性能
随着C++11标准在各大编译器的实现,有了move和rvalue,STL的性能不会是瓶颈,而且另一方面,你的程序既然要最高的性能,为什么要选择C++呢?你还是看重C++的抽象能力(相对于C)对吧?既然你都看重的不仅仅是性能了,那还拿什么性能说事儿?
当然,你也可以自己去实现一套STL的东西(比如EA就自己搞过一套EA STL),但谁来负责进化呢?你要做很多benchmark,写很多test case,还要在项目成员间宣传“用了我的STL,手不疼了,脚不抽经了。。。”,不然你离职后,这坨东西自会被后人慢慢当一坨东西一样扔掉。
有这时间,少百分之几的性能又怎样,陪陪老婆孩子不挺好的吗?
2,对STL不熟悉
如楼上已经有几位知友提到,Server端的程序员大多是从C转过来的,对C++的一些高级特性(包括STL的实现)并没有很好的掌控能力,自然会选择用C的方式来用C++。
3,调试
如果不用STL就好调试了,那为什么还有那么多关于调试的书籍?而且还卖的不错。
三, 理解C++之std 与 stl
1,首先明确 std是一个 命名空间的名字。
2,其次,明确STL是 Standard Template Library的缩写,即标准模板库。
3,2者关系: In fact, all identifiers of the C++ standard library are defined in a namespace called std. 而STL被容纳与C++ Standard Library里。
即2者均属于 C++ Standard Library里。STL是其中的一部分内容,std是作为一个外部的名字。
4,举例 C++一般的库函数 也要#include四, c++0x c++11 几个特性:
原生支持正则表达式?
各种匿名函数?
还有 auto 的一种使用是根据上下文推断数据类型?
五, c++11 STL, C++11 中STL库中新增内容
C++ 11一个比较显著的变化是以前boost库中的一些函数被正式标准化合入到STL中了,本文就简单的介绍一下。
当模板函数参数为泛型类型的时候,无法推导出是传值还是传引用,默认情况下会使用传值的方式。这是我们可以用std::ref显式指定以传引用的方式实例化模板函数。
#include
#include
template <class T>
void foo(T arg)
{
arg++;
}
int main()
{
int count = 3;
foo(count); //此时传的是值,模板实例化为foo(int),count值不变
std::cout << count << std::endl;
foo(std::ref(count)); //此时传的是引用,模板实例化为foo(int&),count值加1
std::cout << count << std::endl;
}
智能指针主要引入了shared_ptr、weak_ptr、unique_ptr三种,其中shared_ptr和weak_ptr就是boost库中的应对象,我以前也写过相关文章介绍他们,这里就不介绍了。
而新引入的unique_ptr和以前介绍过的boost库中的scoped_ptr比较类似,而STL中本身就有一个类似的auto_ptr,它们之间的主要区别是:
相比较而言,unique_ptr权限比auto_ptr小,也没有scope_ptr不能作为返回值的限制,是用起来最合适的,完全可以代替auto_ptr
四个boost库也合入了stl库中:
其中function和bind我以前在介绍boost库中已经介绍过,在支持auto关键字后,通过bind创建function更加简单了,我们只需要用一句话就能创建成员函数的仿函数。
#include
#include
using namespace std;
struct X
{
bool foo (int a) { cout<< a << endl; return false;}
};
int main()
{
X x;
auto func = bind(&X::foo, &x, _1);
func(5);
}
PS:使用bind的时候需要加入std::placeholders的using,否则编译报语法错误。
不过感觉bind基本上被lambda表达式给秒了,对于上面例子里,用lambda表达式的写法如下:
auto func = [&x](int a) { x.foo(a); };
function<void (int)> func = [&x](int a) { x.foo(a); };
由于lambda表达式是语法糖,因此可读性方面更好(感觉简洁度都快接近C#的匿名函数了),没有_1,_2之类的占位符,对于函数的调用方式也是显式直接调用,更加直观。
而result_of在auto引入后感觉也基本上没有用了,直接使用auto要简单得多。
容器方面主要加了如下几个:
其中tuple和array基本上就是boost相关库给标准化了,而unordered_set和unordered_map则是hash表方式的set和map,以提供更高的查询性能。使用方式和原来二叉树版的也大同小异,这里就不多介绍了。
Boost的regex库也终于标准化了,要使用字符串处理的可以不用到处找第三方的正则表达式库了。不过目前VC还不支持像C#那样取消转义字符(gcc可以),在代码里面的正则表达式依然非常难读,希望MS能尽快把raw string literal给支持上。
Boost的线程库也标准化了,另外那个类似于.Net TPL库的packaged_task也标准化了,由于它有不少可以介绍的地方,我会专门写篇文章来介绍它,这里就不多说了。
其实C语言标准库是提供了时间函数的,不过极度难用,现在Boost的时间函数chrono已经给标准化了,虽然还是比不上.Net的TimeSpan好用,但起码比标准C的那套好太多了。
#include
#include
#include
using namespace std;
int fibonacci(int n)
{
if (n < 3) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
int main()
{
auto start = chrono::system_clock::now();
int result = fibonacci(40);
auto end = chrono::system_clock::now();
int elapsed_seconds = chrono::duration_cast
(end-start).count();
auto end_time = chrono::system_clock::to_time_t(end);
std::cout << "result: " << result << endl
<< "finished computation at " << std::ctime(&end_time)
<< "elapsed time: " << elapsed_seconds << "ms\n";
}
另外一个日期函数Boost.Date好像还没有标准化,要用到日期相关功能还是只能用boost库。
Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一。 Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。在C++社区中影响甚大,是不折不扣的“准”标准库。Boost由于其对跨平台的强调,对标准C++的强调,与编写平台无关。大部分boost库功能的使用只需包括相应头文件即可,少数(如正则表达式库,文件系统库等)需要链接库。但Boost中也有很多是实验性质的东西,在实际的开发中实用需要谨慎。