⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 item_20.html

📁 制作本书的目的是为了方便大家的阅读。转载时请保持本电子书的完整性。 前言、条款2、16、21、44根据从Addison-Wesley出版社下载的开放条款翻译。条款26、27、28、45根据从Sc
💻 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>条款20:为指针的关联容器指定比较类型</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><link href="css/all.css" rel="stylesheet" type="text/css" /><link rel="stylesheet" href="http://stl.winterxy.com/styles-site.css" type="text/css" /><link rel="alternate" type="application/rss+xml" title="RSS" href="http://stl.winterxy.com/index.rdf" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://stl.winterxy.com/rsd.xml" /></head><body><div id="banner"><h1><a href="http://stl.winterxy.com/" accesskey="1">Center of STL Study</a> </h1><span class="description">——最优秀的STL学习网站<script language="javascript" src="http://www.winterxy.com/cgi-bin/js/webstats.js"></script></span></div><h2>条款20:为指针的关联容器指定比较类型</h2> <p>假定你有一个string*指针的set,你把一些动物的名字插入进set:</p><pre>set&lt;string*&gt; ssp;						// ssp = “set of string ptrs”ssp.insert(new string(&quot;Anteater&quot;));ssp.insert(new string(&quot;Wombat&quot;));ssp.insert(new string(&quot;Lemur&quot;));ssp.insert(new string(&quot;Penguin&quot;));</pre> <p>然后你写了下列代码打印set的内容,希望字符串按字母顺序出现。毕竟,确定set保持它们的内容有序。</p><pre>for (set&lt;string*&gt;::const_iterator i = ssp.begin();		// 你希望看到			i != ssp.end();			// 这个:“Anteater”			++i)				// “Lemur”,“Penguin”,	cout &lt;&lt; *i &lt;&lt; endl;				// “Wombat”</pre> <p>注释描述了你希望看见的,但你根本没看见。取而代之的是,你看见四个十六进制的数。它们是指针的值。因为set容纳指针,*i不是一个string,它是一个string的指针。让这成为提醒你坚持<a href="item_43.html">条款43</a>的指导并避免自己写循环的一课。如果你已经改为调用copy算法,</p><pre>copy(ssp.begin(), ssp.end(),				// 把ssp中的字符串	ostream_iterator&lt;string&gt;(cout, &quot;\n&quot;));		// 拷贝到cout(但这							// 不能编译)</pre> <p>你将不仅打更少的字符,而且你将很快会查明你的错误,因为这个copy的调用将不能编译,ostream_iterator需要知道被打印的对象的类型,所以当你告诉它是一个string时(通过作为模板参数传递),编译器检测到那和ssp中储存的对象类型(是string*)之间不匹配,它们会拒绝编译代码。获得了额外的类型安全。</p><p>如果你愤怒地把显式循环中的*i改为**i,你<em>可能</em>可以得到你想要的输出,但也可能不。是的,动物名字将被打印,但它们按字母顺序出现的机会只是24份之1。ssp保持它的内容有序,但是它容纳的是指针,所以它以<em>指针的值</em>排序,而不以string值。对于四个指针值可能有24种排列<strong>(译注:4! = 4 * 3 * 2 * 1 = 24)</strong>,所以指针被储存时有24种可能的顺序。因此你看见字符串按字母排序有24份之1的几率。<sup><a id="Note1Back" href="#Note1">[1]</a></sup></p><p>为了克服这个问题,你应该回忆起</p><pre>set&lt;string*&gt; ssp; </pre> <p>是这个的简写:</p><pre>set&lt;string*, less&lt;string*&gt; &gt; ssp; </pre> <p>好,为了完全准确,它是</p><pre>set&lt;string*, less&lt;string*&gt;, allocator&lt;string*&gt; &gt; ssp; </pre> <p>的简化,但是分配器在本条款里与我们无关,所以我们将忽略它们。</p><p>如果你想要string*指针以字符串值确定顺序被储存在set中,你不能使用默认比较仿函数类less&lt;string*&gt;。你必须改为写你自己的比较仿函数类,它的对象带有string*指针并按照指向的字符串值来进行排序。就像这样:</p><pre>struct StringPtrLess:	public binary_function&lt;const string*,		// 使用这个基类				const string*,		// 的理由参见<a href="item_40.html">条款40</a>				bool&gt; {	bool operator()(const string *ps1, const string *ps2) const	{		return *ps1 &lt; *ps2;	}};</pre> <p>然后你可以使用StringPtrLess作为ssp的比较类型:</p><pre>typedef set&lt;string*, StringPtrLess&gt; StringPtrSet;StringPtrSet ssp;					// 建立字符串的集合,						// 按照StringPtrLess定义的顺序排序...						// 和前面一样插入						// 同样四个字符串</pre> <p>现在你的循环最后将做你想要它做的(也就是前面你使用*i代替**i所修正的问题):</p><pre>for (StringPtrSet::const_iterator i = ssp.begin();	// 打印“Anteater”,		i != ssp.end();			// “Lemur”		++i)				// “Penguin”	cout &lt;&lt; **i &lt;&lt; endl;			// “Wombat”</pre> <p>如果你想要改为使用算法,你可以写一个知道怎么在打印string*指针之前对它们解引用的函数,然后和for_each联用那个函数:</p><pre>void print(const string *ps)			// 把ps指向的{						// 对象打印到cout	cout &lt;&lt; *ps &lt;&lt; endl;}for_each(ssp.begin(), ssp.end(), print);		// 在ssp中的每个						// 元素上调用print</pre> <p>或者你想象并写出了泛型的解引用仿函数类,然后让它和transform与ostream_iterator连用:</p><pre>// 当本类型的仿函数被传入一个T*时,它们返回一个const T&amp;struct Dereference {	template &lt;typename T&gt;	const T&amp; operator()(const T *ptr) const	{		return *ptr;	}};transform(ssp.begin(), ssp.end(),				// 通过解引用“转换”		ostream_iterator&lt;string&gt;(cout, &quot;\n&quot;),	// ssp中的每个元素,		Dereference());				// 把结果写入cout</pre> <p>但是,用算法代替循环不是要点,至少对于本条款来说是这样的。(它是<a href="item_43.html">条款43</a>的要点。)要点是无论何时你建立一个指针的标准关联容器,你必须记住容器会以指针的值排序。这基本上不是你想要的,所以你几乎总是需要建立自己的仿函数类作为比较类型。</p><p>注意到我写的是“比较<em>类型</em>”。你可能奇怪为什么必须特意创造一个仿函数类而不是简单地为set写一个比较函数。例如,你可能想试试:</p><pre>bool stringPtrLess(const string* ps1,		// 将成为用于			const string* ps2)		// 按字符串值{						// 排序的string*指针	return *ps1 &lt; *ps2;			// 的比较函数}set&lt;string*, stringPtrLess&gt; ssp;			// 假设使用stringPtrLess						// 作为ssp的比较函数;						// <em>这不能编译</em></pre> <p>这里的问题是每个set模板的第三个参数都是一种<em>类型</em>。令人遗憾的是,stringPtrLess不是一种类型,它是一个函数。这就是为什么尝试使用stringPtrLess作为set的比较函数不能编译的原因,set不要一个函数,它要的是能在内部用实例化<em>建立</em>函数的一种类型。</p><p>无论何时你建立指针的关联容器,注意你也得指定容器的比较类型。大多数时候,你的比较类型只是解引用指针并比较所指向的对象(就像上面的StringPtrLess做的那样)。鉴于这种情况,你手头最好也能有一个用于那种比较的仿函数模板。像这样:</p><pre>struct DereferenceLess {	template &lt;typename PtrType&gt;		bool operator()(PtrType pT1,		// 参数是值传递的,				PtrType pT2) const		// 因为我们希望它们		{					// 是(或行为像)指针			return *pT1 &lt; *pT2;		}};</pre> <p>这样的模板消除了写像StringPtrLess那样的类的需要,因为我们可以改为使用DereferenceLess:</p><pre>set&lt;string*, DereferenceLess&gt; ssp;		// 行为就像					// set&lt;string*, StringPtrLess&gt;</pre> <p>噢,还有一件事。本条款是关于指针的关联容器,但它也可以应用于表现为指针的对象的容器,例如,智能指针和迭代器。如果你有一个智能指针或迭代器的关联容器,那也得为它指定比较类型。幸运的是,指针的这个解决方案也可以用于类似指针的对象。正如DereferenceLess适合作为T*的关联容器的比较类型一样,它也可以作为T对象的迭代器和智能指针容器的比较类型。</p><hr /> <p><sup><a id="Note1" href="#Note1Back">[1]</a></sup> 实际上,这24种排列很可能不是平等的,所以“24份之1”的陈述有点使人误解。确实,有24个不同的顺序,而且你可能得到它们中的任何一个。</p></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -