📄 appendix_b.html
字号:
<?xml version="1.0" encoding="gb2312"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>附录B:在微软STL平台上的注意事项</title><link href="css/all.css" rel="stylesheet" type="text/css" /><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /></head><body><h1>在微软STL平台上的注意事项</h1><p>在这本书的开头几页里,我提到了术语<dfn>STL平台</dfn>是指一个特定编译器和一个标准模板库特定实现的组合。如果你在使用版本6或更早的Microsoft Visual C++编译器(即,伴随版本6或更早的Microsoft Visual Studio的编译器),在编译器和库之间的区别特别重要,因为编译器有时比伴随的STL实现更有能力。在本附录中,我描述了旧的微软STL平台的一个重要缺点,而且我提供可以明显改进你STL经验的变通办法。</p><p>下面是给使用Microsoft Visual C++(MSVC)4-6版的开发者的信息。如果你正使用Visual C++ .NET,你的STL平台没有下面描述的那些问题,你可以略过这个附录。</p><h2>STL里的成员函数模板</h2><p>假设你有两个Widget的vector,你想把一个vector里的Widget拷贝到另一个的末端。那很容易。只要使用vector的区间insert函数(参见<a href="item_05.html">条款5</a>):</p><pre>vector<Widget> vw1, vw2;...vw1.insert(vw1.end(), vw2.begin(), vw2.end()); // 把vw2里Widget的副本 // 追加到vw1</pre><p>如果你有一个vector和一个deque,你一样可以这么做:</p><pre>vector<Widget> vw;deque<Widget> dw;vw.insert(vw.end(), dw.begin(), dw.end()); // 把dw里Widget的副本 // 追加到vw</pre><p>实际上,你不必理会被拷贝的容器容纳的是什么类型的对象。即使自定义容器也可以工作:</p><pre>vector<Widget> vw;...list <Widget> lw;...vw.insert(vw.begin(), lw.begin(), lw.end()); // 把lw里Widget的副本 // 追加到vwset<Widget> sw;...vw.insert(vw.begin(), sw.begin(), sw.end()); // 把sw里Widget的副本 // 追加到vwtemplate<typename T, // 用于自定义 typename Allocator = allocator<T> > // 兼容STL的class SpecialContainer { ... }; // 容器模板SpecialContainer<Widget> scw;...vw.insert(vw.end(), scw.begin(), scw.end()); // 把scw里Widget的副本 // 追加到vw</pre><p>这种灵活性是可能的,因为vector的区间insert函数完全不是一个函数。相反,它是一个<dfn>成员函数模板</dfn>,可以用任何<em>迭代器类型</em>实例化,以产生一个具体的区间insert函数。对于vector,标准像这样声明insert模板:</p><pre>template <class T, class Allocator = allocator<T> >class vector {public: ... template <class InputIterator> void insert(iterator position, InputIterator first, InputIterator last); ...};</pre><p>每个标准容器都要求提供这个模板化的区间insert。容器也要求提供类似成员函数模板的区间构造函数和assign的区间形式(两者都在<a href="item_05.html">条款5</a>讨论)。</p><h2>MSVC版本4-6</h2><p>不幸的是,伴随MSVC版本4-6的STL实现没有声明成员函数模板。这个库最初为MSVC版本4开发,而那个编译器,像它所在时代的大多数编译器一样,缺乏成员函数模板的能力。在MSVC4到MSVC6之间,编译器增加了对这些模板的支持,但是,由于法律诉讼的影响,微软没有直接包含它们,库基本保持冻结。</p><p>因为伴随MSVC4-6的STL实现是为一个缺乏成员函数模板的编译器设计的,库的作者通过用具体函数替换每个模板的方法来近似这样的功能性,也就是只接受和容器的迭代器类型相同的迭代器。例如,对于insert,这个成员函数模板被替换这样:</p><pre>void insert(iterator position, // “iterator”是 iterator first, iterator last); // 容器的迭代器类型</pre><p>受限的区间成员函数形式可以进行从一个vector<Widget>到一个vector<Widget>或从一个list<int>到一个list<int>的区间插入,但不能从一个vector<Widget>到一个list<Widget>或从一个set<int>到一个deque<int>。甚至不可能进行从一个vector<long>到一个vector<int>的区间insert(或assign或构造),因为vector<long>::iterator与vector<int>::iterator类型不同。结果,下面十分有效的代码不能用MSVC4-6编译:</p><pre>istream iterator<Widget> begin(cin), end; // 建立用于从cin // 读取Widget的 // begin和end迭代器 // (参见<a href="item_06.html">条款6</a>)vector<Widget> vw(begin, end); // 把cin的Widget读入vw // (再次参见<a href="item_06.html">条款6</a>);<em>在MSVC4-6</em> // <em>不能编译</em>list<Widget> lw;...lw.assign(vw.rbegin(), vw.rend()); // 把vw的内容赋值给lw // (以反序);<em>在MSVC4-6</em> // <em>不能编译</em>SpecialContainer<Widget> scw;...scw.insert(scw.end(), lw.begin(), lw.end()); // 把lw中的Widget的副本 // 插入scw的末端; // <em>在MSVC4-6不能编译</em></pre><p>那如果你必须使用MSVC4-6,你该怎么办?那取决于你使用的MSVC版本和是否你被迫使用伴随编译器的STL实现。</p><h2>MSVC4-5的变通办法</h2><p>再次看看不能用伴随MSVC4-6的STL编译的有效代码例子:</p><pre>vector<Widget> vw(begin, end); // 被MSVC4-6的 // STL实现拒绝list<Widget> lw;...lw.assign(vw.rbegin(), vw.rend()); // 也拒绝SpecialContainer<Widget> scw;...scw.insert(scw.end(), lw.begin(), lw.end()); // 同上</pre><p>这些调用看起来相当不同,但它们全都由于相同的原因而失败:在STL实现里缺乏成员函数模板。对它们有一种单独的治疗方法:使用copy和插入迭代器(参见<a href="item_30.html">条款30</a>)。例如,这里是上面例子的变通办法:</p><pre>istream_iterator<Widget> begin(cin), end;vector<Widget> vw; // 默认构造vw;copy(begin, end, back_inserter(vw)); // 然后把cin中的 // Widget拷贝进去list<Widget> lw;...lw.clear(); // 去除lw的老copy(vw.rbegin(), vw.rend(), back_inserter(lw)); // Widget;把 // vw的Widget拷贝进去(以 // 反序)SpecialContainer<Widget> scw;...copy(lw.begin(), lw.end(), // 把lw的Widget拷贝到 inserter(scw, scw.end())); // scw的结尾</pre><p>我鼓励你在伴随MSVC4-5的库上使用这样的基于copy的变通办法,但是注意!不要满足于这个变通办法,你忘记了它们只是<em>变通办法</em>。正如<a href="item_05.html">条款5</a>解释的,使用copy算法几乎总是不如使用一个区间成员函数,所以一旦你有机会把你的STL平台升级到支持成员函数模板的版本,就在区间成员函数是正确方法的地方停止使用copy。</p><h2>用于MSVC6的另一个变通办法</h2><p>你也可以对MSVC6使用MSVC4-5的变通办法,但对于MSVC6有另一个选择。作为MSVC4-5一部分的编译器没有提供有意义的成员函数模板,所以STL实现缺乏它们的事实是无关紧要的。MSVC6的形势则不同,因为MSVC6的编译器支持成员函数模板。因此有理由考虑用提供标准指定的成员函数模板的STL实现替换伴随MSVC6的。</p><p><a href="item_50.html">条款50</a>解释了SGI和STLport都提供了可以自由下载的STL实现,而且那两个实现都把MSVC6编译器作为将配合的编译器之一。你也可以从Dinkumware购买最新的兼容MSVC的STL实现。每种选择各有利弊。</p><p>SGI的和STLport的实现是自由的,我想你知道那在对软件的官方支持上代表什么:完全没有。而且,因为SGI和STLport把他们的库设计为使用多种编译器,你或许必须手工配置它们的实现来最有效地使用MSVC6。特别是,你可能必须明确地启用成员函数模板的支持,因为,它们要使用很多编译器,SGI和/或STLport默认可能不启用它。你可能也得为与其他MSVC6库(特别是DLL)链接而担心,包括保证你使用合适的线程和调试构建,等等。</p><p>如果你被那些事吓着了,或如果你听过你负担不起自由软件的牢骚,你可能要看看Dinkumware用于MSVC6的替代库。它被设计为提高原生MSVC6 STL的兼容性,并使作为STL平台的MSVC6对标准的支持最大化。因为Dinkumware写了伴随MSVC6的STL,所以他们最新的STL实现有很大的可能性真的<em>是</em>一个合适的替代品。要了解更多关于Dinkumware STL实现的信息,访问公司的网站:<a href="http://www.dinkumware.com/">http://www.dinkumware.com/</a>。</p><p>不管你选择的是SGI的、STLport的还是Dinkumware的实现作为STL的替代品,你将得到的不只是带有成员函数模板的STL。你也将在库的其他地方旁路一致性问题,比如没有声明push_back的string。此外,你可以访问有用的STL扩展,包括散列容器(参见<a href="item_25.html">条款25</a>)和单链表(slists)。SGI的和STLport的实现也提供了多种非标准的仿函数类,比如select1st和select2nd(参见<a href="item_50.html">条款50</a>)。</p><p>即使你被伴随MSVC6的STL实现困住,访问Dinkumware网站或许也是值得的。那个网站列举了在MSVC6的库实现里的已知漏洞并解释怎样修改你的库副本来减少它的缺陷。不用说,编辑你的库头文件是让你自己冒险的事。如果你遇到麻烦,不要责备我。</p></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -