为何某些公司不允许使用C++ STL?
说几个STL的缺点吧,虽然都是在比较极端的情况下出现,但是对于一些大项目还是会遇到的
1. 代码膨胀问题
每一个实例化过的模板类,都会膨胀出一份独立的代码,比如
std::vector<std::string>, std::vector<int>,编译后会产生两份代码,在VC2008下,每份代码大约是3-4kb,这是因为vector比较简单代码少,如果是map则会产生30-50kb的代码,因为map里有个复杂的红黑树。对于数据处理类的代码里一般会定义很多种不同的结构体,不同的结构体放到不同的容器里,就会实例化出很多个类的代码,我见过一个项目里,这样的vector就有数百个。
2. 内存使用效率问题 (以vc++2008为例)
stl在内存使用效率上是比较低效的,比如std::string,它的sizeof大概是28,因为它有一个内置的16字节数组,用来做小字符串优化的,就是说低于16字节的字符串都会至少占用28字节内存,如果刚好17字节字符串,则会占用28字节+额外分配的字符串内存,额外分配的内存是一个堆块,又有很多浪费,相比用一个char *存储字符串大约多占用了一倍内存。
还有map<>,每一个map的node都是一块独立分配的内存,如果是 map<int, int>呢,那就很悲剧了,为了存一个int要消耗几十个字节,很浪费的。
如果元素数量有百万级,那么内存占用就很可观了,这种情况下建议自己实现allocator,做内存池。
3. deep copy问题
让两个容器的实例做赋值操作,看起来就一条语句,实际上容器里的每个元素都执行了一次赋值操作。如果容器里有百万级的数据,那么一个等号就产生了几百万次的构造和析构。
传递参数的时候一定要用 const 引用,赋值可以用 swap代替。
4. 隐式类型转换
比如 有个函数
void doSomething(const std::string &str);
调用的时候
doSomething("hello");
能编译执行,但是会产生一个临时的匿名的std::string实例,把"hello"复制一遍,然后在调用完成后析构掉。如果这个发生在循环体内部有可能影响性能。
以上这些问题,在小程序里或者数据规模不大的时候,比如容器内元素只有几千这个规模,都不是什么大问题,那时开发效率才是重点,但是一旦有大数据stl容器会成为性能瓶颈的。
我并不是主张不用STL,而是要充分了解STL的优缺点,根据应用场景做选择。
***********************************************************************************************************************************************************
最初开始禁用 C++ STL,更多地是早期项目编码实践中留下的惯例,被后来的程序员继承下来。老项目中这种选择尤其地多。不过如果有人将其上升到公司行为在不同项目中全面禁用 STL,则没有必要,而且我倾向于做这种决定的人并不理解 C++ 编译系统。
一般来说,项目中禁用 C++ 多见于两种具体场景:或者项目的产出产品为函数库,或者需要引用第三方函数库。具体地来说,有三个主要原因:
第一个原因是二进制边界混乱。对需要在项目中使用第三方函数库的程序员来说,二进制边界是个头痛的问题。C++ 在这一方面本身就处理得不算好,加上模板后起到的是雪上加霜的后果。没有经验的程序员会贪图方便而在公开头文件中使用 C++ 模板,如果这时调用方的编译器选项设置或 STL 版本和编译方不同,那么就可能出现同样的头文件在不同的环境下二进制布局不符的情况。——顺便说一句,在过去十年里,各个主流编译器附带的 STL 版本变化节奏不慢,所以这种由于编译环境不同而导致的 bug 并不算罕见,但缺乏汇编知识的用户难以排查。
第二个原因是不愿使用异常。如今除了 Android 上的 STLPort 关闭异常,大部分主流 C++ STL 实现里都无法脱离异常使用 STL。异常带来的问题主要是两个:性能下降,代码膨胀。这几年 C++ 编译器在性能方面的改进很多,good path 的性能问题已经基本没有,但代码膨胀问题却没有太多改善,甚至这个性能问题的一部分解决方案就是以代码膨胀为代价。我写过一篇短文比对过 Android 上 gcc 4.6 在有无异常的情况下的汇编代码逻辑,可以看到,启动异常时生成的汇编代码量多出了相当一部分(我的例子中是 50%),用于处理各种隐含代码中的异常问题。这一条在手机系统中有时候会引起意想不到的麻烦,比如软件升级后导致 app 在低存储容量的手机中安装失败。顺便说一句,这个问题并不是 gcc 独有,clang 上生成的代码是一样的。参考:http://dummydigit.net/posts/2014-01-01-23-30-1.html。
最后一个原因是 C 兼容。严格地说,STL 在这个问题上算是躺枪,这个坑在很多具体的场景中也是因为异常而引入,但这个问题的麻烦程度比前两个问题更高。比如 gcc 在编译纯 C 代码时默认关闭 -fexceptions 选项,因此这样编译出来的代码中没有异常处理相关的栈展开。如果某个 C++ 项目引用了一个第三方 C 项目,它很难确保那个 C 项目给出的二进制代码中正确进行了异常处理并保证代码服从异常安全操作。这种场景下混用 C/C++ ,就可能在抛出异常时莫名其妙地崩溃或者出现 C 代码区段中的资源泄漏,特别是 expat 那种大量利用回调的代码结构。要规避这种风险并非不可能,但需要 C 的架构部分做修改,比如使用 DOM 那种树形结构,这种做法对历史项目而言又很难办到。换言之,如果一个项目出于种种原因需要保持 C 兼容,而 STL 就属于其中一个不可控的变数,与其相信程序员不犯错,不如直接禁用更可控一些。参考:Code Gen Options
要解决二进制相关的问题很简单:整个项目的所有相关代码在同一个代码基上编译,强制打开编译选项添加异常代码,并去除一切二进制依赖。但对很多小公司来说,引入这样的系统对配置管理的要求较高。如果一部分依赖关系来自自己并不了解的第三方代码,轻易修改编译选项可能带来的风险与第三方代码库的规模成正比。退一步说,即便团队里真的有强大的配置管理工程师能够搞定一切,他们也不会有能力解决代码膨胀问题,除非他们有权决定换一个编译器。相比之下,前面朋友所说的所谓性能或者编译出错时糟糕的可读性,在我看来反倒是次要因素,而且这些缺陷都正在新的编译器中逐步得到解决或改善,比如 clang。
所以那些选择禁用 STL 的早期项目负责人,总有一些确实的理由,没有人那么别扭地想跟开发效率过不去。至于后来的人是真的仔细想过这些细节还是人云亦云,那是另一个问题。
****************************************************************************************************************************************************************
一些普遍解决方案和回答:
最安全的方式是不要跨 DLL boundary 使用 C++ 对象,尤其是 STL。
DLL 和调用 DLL 程序使用的是不是一个堆,取决于整个工程的 CRT 使用方式。如果 CRT 是动态链接的,那么程序本身和所有 DLL 都使用一个堆。如果 CRT 是静态链接,那么必然是逐一静态链接到程序本身和每个 DLL 上,那么每个模块都是一个单独的堆。
c++ - How can I use Standard Library (STL) classes in my dll interface or ABI?
一个原则是,你的DLL公共类的ABI必须是确定的,除非你自己改代码,否则这个ABI不应该随着DLL的编译器版本而改变,但是STL做不到这一点,因为他的实现不受DLL作者控制。但是STL可以在DLL私有部分使用,只要其细节不被暴露出来即可。
推荐文章:"How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object“ - 微软
可以,要注意一些细节,先占坑
已填坑
爪机码字一大段直接没了,我的心好痛
1.exe和dll全部使用动态链接
因为dll和exe是链接到同一个运行库dll上的,因此使用的是同一份运行库的代码,内存分配是在一个堆上的。对于所有类所有特性都能跨二进制文件使用
2.只要任意一个使用了静态链接,你就不能向上面一样使用
原因:静态链接会在生成的二进制文件里包含库的代码,那么你的dll和exe使用的其实是两个运行库的代码,自然会有两个不同堆的问题
解决方案:
1.对于dll中导出的对象避开所
有malloc(new)和free(delete)操作,dll中使用exe里的对象也一样
2.将exe里和dll里的malloc(new)和free(delete)地址弄出来,人工避免混用
3.利用c++虚函数。由于虚函数的地址是在运行期绑定的,因此你可以利用这个特性来避免混用exe和dll中的malloc(new)和free(delete)。比如你在dll里new了一个class a并在exe里使用它,那你就不能在exe里直接delete之。你应该为a添加一个virtual的release
virtual void release()
{
delete this;
}
需要释放资源的时候调用release。对于所有需要申请内存的操作同理。
然而对于string在静态链接的情况下除了解决方法1都没有什么卵用,除非你自己实现一个。
references:
http://www.zhihu.com/question/20201972
http://www.zhihu.com/question/32127579
为何某些公司不允许使用C++ STL?的更多相关文章
- 我的STL学习之路
说起STL(标准模板库),相信了解C++的都不会陌生吧^_^.LZ是从大三开始学习C++(ps:不是科班出身),并慢慢接触使用STL的,在学校中使用STL比较多的情况是写数据结构代码,使用STL实现数 ...
- STL学习小结
STL就是Standard Template Library,标准模板库.这可能是一个历史上最令人兴奋的工具的最无聊的术语.从根本上说,STL是一些"容器"的集合,这些" ...
- STL学习总结
STL就是Standard Template Library,标准模板库.这可能是一个历史上最令人兴奋的工具的最无聊的术语.从根本上说,STL是一些"容器"的集合.这些" ...
- 关于 C++ STL
一.STL简介 STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称.它是由Alexander Stepanov.Meng Lee和David R ...
- STL 较详尽总结
STL就是Standard Template Library,标准模板库.这可能是一个历史上最令人兴奋的工具的最无聊的术语.从根本上说,STL是一些"容器"的集合,这些" ...
- C/C++ 经典面试题汇总
面试题1:变量的声明和定义有什么区别 ? 为变量分配地址和存储空间的称为定义,不分配地址的称为声明.一个变量可以在多个地方声明,但是只在一个地方定义.加入extern修饰的是变量的声明,说明此变量将在 ...
- C++ STL简述
前言 最近要找工作,免不得要有一番笔试,今年好像突然就都流行在线笔试了,真是搞的我一塌糊涂.有的公司呢,不支持Python,Java我也不会,C有些数据结构又有些复杂,所以是时候把STL再看一遍了-不 ...
- 各大IT公司校园招聘程序猿笔试、面试题集锦
转自:http://blog.csdn.net/hackbuteer1/article/details/7959921#t4 百度一面 1.给定一个字符串比如“abcdef”,要求写个函数编程“def ...
- STL学习之路
本文面向的读者:学习过C++程序设计语言(也就是说学习过Template),但是还没有接触过STL的STL的初学者.这实际上是我学习STL的一篇笔记,老鸟就不用看了. 什么是泛型程序设计 我们可以简单 ...
随机推荐
- ORACLE的RMAN
1.什么是RMAN? RMAN可以用来备份和还原数据库文件.归档日志和控制文件.它也可以用来执行完全或不完全的数据库恢复. 注意:RMAN不能用于备份初始化参数文件和口令文件. RMAN启动数据库上的 ...
- 关于C#泛型列表List<T>的基本用法总结
//示例代码如下:using System;using System.Collections.Generic;using System.Collections.ObjectModel;namespac ...
- java异常面试常见题目
在Java核心知识的面试中,你总能碰到关于 处理Exception和Error的面试题.Exception处理是Java应用开发中一个非常重要的方面,也是编写强健而稳定的Java程序的关键,这自然使它 ...
- Python学习笔记2-flask-sqlalchemy 简单笔记
flask-sqlalchemy 简单笔记 字数 阅读 评论 喜欢 flask-sqlalchemy SQLAlchemy已经成为了python世界里面orm的标准,flask是一个轻巧的web框架, ...
- 简单的jQuery日期选择
建立一个存放日期的文本框 点击文本框弹出一个选择框引用jQuery文件引用bootstrap文件 使用模态框 使用模态框的方法设置点击文本框显示模态框手动打开模态框的方法:$('id').modal( ...
- SQL 结构化查询语言手册
摘自该学习网站: http://www.w3school.com.cn/sql/ 新学到的几点: and 和or 连用,记得用括号. 2.SQL通配符补充 例如: ...
- linux的常用命令及常用快捷键
常用指令 ls 显示文件或目录 -l 列出文件详细信息l(list) -a 列出当前目录下所有文件及目录,包括隐藏的a(all) mkdir ...
- Html.RenderPartial与Html.RenderAction区别(转)
Html.RenderPartial与Html.RenderAction这两个方法都是用来在界面上嵌入用户控件的. Html.RenderPartial是直接将用户控件嵌入到界面上: <%Htm ...
- MySQL忘记root密码的解决方案
在实际操作中忘记MySQL的root密码是一件令人很头痛的事情,不要急以下的文章就是介绍MySQL的root密码忘记的时候解决方案,我们可以对其进行如下的步骤重新设置,以下就是文章的详细内容描述. ...
- WPF自定义Main函数
第一步:在app.xaml.cs里加入以下代码: [STAThread] public static void Main(string[] args) { App app = new App(); a ...