Effective STL 条款16

原创 2004年07月21日 21:02:00

条款16: 如何将vectorstring的数据传给传统的API<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

因为 C++语言已经于1998年被标准化,C++的中坚分子在努力推动程序员从数组转到vector时就没什么顾虑了。同样显然的情况也发生于尝试使开发者从char*指针迁移到string对象的过程中。有很好的理由来做这些转变,包括可以消除常见的编程错误(参见条款13),而且有机会获得STL算法的全部强大能力(比如参见条款31)。

但是,障碍还是有的,最常见的一个就是已经存在的传统C风格的API接受的是数组和char*指针,而不是vectorstring对象。这样的API函数还将会存在很长时间,如果我们要高效使用STL的话,就必须和它们和平共处。

幸运的是,这很容易。如果你有一个vector对象v,而你需要得到一个指向v中数据的指针,以使得它可以被当作一个数组,只要使用&v[0]就可以了。对于string对象s,相应的咒语是简单的s.c_str()。但是是只读的。如广告中难懂的条文时常指出的,必然会有几个限制。

给定一个

表达式v[0]生产一个指向vector中第一个元素的引用,所以,&v[0]是指向那个首元素的指针。vector中的元素被C++标准限定为存储在连续内存中,就像是一个数组,所以,如果我们想要传递v给这样的C风格的API

我们可以这么做:

也许吧。可能吧。唯一的问题就是,如果v是空的。如果这样的话,v.size()0,而&v[0]试图产生一个指向根本就不存在的东西的指针。这不是件好事。其结果未定义。一个较安全的方法是这样:

如果走错路了,你可能会碰到一些半吊子的人物,他们会告诉你说可以用v.begin()代替&v[0],因为(这些讨厌的家伙将会告诉你)begin返回指向vector内部的迭代器,而对于vector,其迭代器实际上是指针。那经常是正确的,但正如条款50所说,并不总是如此,你不该依赖于此。begin的返回类型是iterator,而不是一个指针,当你需要一个指向vector内部数据的指针时绝不该使用begin。如果你基于某些原因决定键入v.begin(),就应该键入&*v.begin(),因为这将会产生和&v[0]相同的指针,这样可以让你有更多的打字机会而且让其他要弄懂你代码得人感觉到更晦涩。坦白地说,如果你正在和告诉你使用v.begin()代替&v[0]的人打交道的话,你该重新考虑一下你的社交圈了。(译注:在VC6中,如果用v.begin()代替&v[0],编译器不会说什么,但在VC7GCC中这么做的话,就会引发一个编译错误)

类似从vector上获取指向内部数据的指针的方法,对string不是可靠的,因为(1string中的数据并没有承诺被存储在连续内存中,(2string的内部表示形式并没承诺以一个null字符结束。这解释了string的成员函数c_str存在的原因,它返回一个按C风格设计指针,指向string的值。因此我们可以这样传递一个string对象s给这个函数,

象这样:

即使是字符串的长度为0,它都能工作。在那种情况下,c_str将返回一个指向null字符的指针。即使字符串内部自己内含null时,它同样能工作。但是,如果真的这样,doSomething很可能将第一个内含的null解释为字符串结束。string对象不在意是否容纳了结束符,但基于char*C风格API在意。

再看一下doSomething的声明:

在两种形式下,指针都被传递为指向const的指针。vectorstring的数据只能传给只读取而不修改它的API。这到目前为止都是最安全的事情。对于string,这也是唯一可做的,因为没有承诺说c_str产生的指针指在string数据的内部表示形式上;它可以返回一个指针指向数据的一个不可修改的拷贝,这个拷贝满足C风格API对格式的要求。(如果这个恐吓令你毛骨悚然的话,还请放心吧,因为它也许不成立。我没听说目前哪个库的实现使用了这个自由权的。)

对于vector,有更多一点点灵活性。如果你将v传给一个修改其元素的C风格API的话,典型情况都是没问题,但被调用的函数绝不能试图改变vector中元素的个数。比如,它绝不能试图在vector还未使用的容量上“创建”新的元素。如果这么干了,v将会变得内部状态不一致,因为它再也不知道自己的正确大小了。v.size()将会得到一个不正确的结果。并且,如果被调用的函数试图在一个大小和容量(参见条款14)相等的vector上追加数据的话,真的会发生灾难性事件。我甚至根本就不愿去想象它。实在太可怕了。

你注意到我在前面的“典型情况都是没问题”那句话用的是“典型地”一词吗?你当然注意到了。有些vector对其数据有些额外的限制,而如果你把一个vector传递给需要修改vector数据的API,你一定要确保这些额外限制继续被满足。举个例子,条款23解释了排序的vector往往是一种可行的选择来实现关联容器,但对这些vector而言,保持顺序非常重要。如果你将一个排序的vector传给一个可能修改其数据的API函数,你需要重视vector在调用返回后不再保持顺序的情况。

如果你想用C风格API返回的元素初始化一个vector,你可以利用vector和数组潜在的内存分布兼容性将存储vecotr的元素的空间传给API函数:

这个技巧只能工作于vector,因为只有vector承诺了与数组具有相同的潜在内存分布。但是,如果你想用来自C风格API的数据初始化string对象,也很简单。只要让API将数据放入一个vector<char>,然后从vector中将数据拷到string:

事实上,让C风格API将数据放入一个vector,然后拷到你实际想要的STL容器中的主意总是有效的:

此外,这也提示了vectorstring以外的STL容器如何将它们的数据传给C风格API。只要将容器的每个数据拷到vector,然后将它们传给API

你也可以将数据拷进一个数组,然后将数组传给C风格的API,但你为什么想这样做?除非你在编译期就知道容器的大小,否则你不得不分配动态数组,而条款13解释了为什么你应该总是使用vector来取代动态分配的数组。

《Effective STL》条款3-条款4

条款3使容器里对象的拷贝操作轻量而正确 条款4用empty来代替检查size是否为0
  • KangRoger
  • KangRoger
  • 2015-10-08 23:31:54
  • 948

Effective C++ 条款9

绝不在构造和析构过程中调用virtual函数本节有个核心的知识点就是在构造函数和析构函数中,virtual函数失去多态性。 试想一下,假设此时在构造函数和析构函数中,virtual函数没...
  • u011058765
  • u011058765
  • 2015-06-22 11:15:54
  • 577

Effective STL 中文版(完整版)

Winter总算找到《Effective STL》的完整中文版了,奉献给大家。书中作者解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案,也...
  • sdnxiaotao
  • sdnxiaotao
  • 2008-07-05 13:23:00
  • 504

Effective STL 条款9

在删除选项中仔细选择本节核心内容: 如何高效的删除容器中的指定数据结论如下: 1.去除一个容器中有特定值的所有对象如果容器是vector,string或者deque,使用erase-remove惯...
  • u011058765
  • u011058765
  • 2016-04-15 08:59:34
  • 176

Effective STL 条款3

确保容器中的对象拷贝正确而高效在使用STL模板的时候,我们必须想到有关拷贝的问题。如果STL中存储的是我们自定义类型。可能会发生以下几个问题:首先为了避免拷贝过程成为程序运转的瓶颈,我们选择存储自定义...
  • u011058765
  • u011058765
  • 2015-06-21 21:13:01
  • 486

《Effective C++》:条款44-条款45

条款44将与参数无关的代码抽离templates 条款45运用成员函数模板接受所有兼容类型...
  • KangRoger
  • KangRoger
  • 2015-03-12 22:01:36
  • 1566

Effective STL条款17-条款18

条款17:使用交换技巧来修正过剩容量本节条款告诉我们,如果你有一个vector的容器,容器的容量是10000,但是,现在只用了1,那么为了节省内存,我们应该只保留使用的vector容量,多余的容量应该...
  • u011058765
  • u011058765
  • 2016-04-21 09:10:41
  • 302

effective C++ 条款十六解读

成对的使用new和delete时要采用相同的形式 使用new 要搭配 delete 使用new [] 要搭配 delete [] ,究其原因主要在于 new  与new [] 申请的内存结构是不同的...
  • weixin_38911779
  • weixin_38911779
  • 2017-08-20 15:21:49
  • 129

《Effective C++》资源管理:条款16-条款17

在使用new和delete要注意的事项,以及使用智能指针时要注意的事项。
  • KangRoger
  • KangRoger
  • 2015-01-16 21:36:23
  • 1401

More Effective C++ 条款16

效率我怀疑一些人在C++软件开发人员身上进行秘密的巴甫洛夫试验,否则为什么当提到“效率”这个词时,许多程序员都会流口水。(Scott Meyers真幽默  译者注)事实上,效率可不是一个开玩笑的事情。...
  • zhc
  • zhc
  • 2001-10-14 23:36:00
  • 662
收藏助手
不良信息举报
您举报文章:Effective STL 条款16
举报原因:
原因补充:

(最多只允许输入30个字)