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

📄 8859.htm

📁 C++细节解释
💻 HTM
字号:
<HTML>
<HEAD>
<meta http-equiv='Content-Type' content='text/html; charset=gb2312'>


<style >
.fst{padding:0px 15px;width:770px;border-left:0px solid #000000;border-right:0px solid #000000}
.fstdiv3 img{border:0px;border-right:8px solid #eeeecc;border-top:6px solid #eeeecc}
</style>
<title>
Effective C++ 2e Item24
</title>
</HEAD>
<BODY >
<center>

<div align=center><div class=fst align=left><div class=fstdiv3 id=print2>
<b>
Effective C++ 2e Item24&nbsp;</b><p>条款24: 在函数重载和设定参数缺省值间慎重选择</p> 
<p>会对函数重载和设定参数缺省值产生混淆的原因在于,它们都允许一个函数以多种方式被调用:</p> 
<p>void f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // f被重载<br>void f(int x);</p> 
<p>f();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用f()<br>f(10);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用f(int)</p> 
<p>void g(int x = 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // g 有一个<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 缺省参数值</p> 
<p>g();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用g(0)<br>g(10);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用g(10)</p> 
<p>那么,什么时候该用哪种方法呢?</p> 
<p>答案取决于另外两个问题。第一,确实有那么一个值可以作为缺省吗?第二,要用到多少种算法?一般来说,如果可以选择一个合适的缺省值并且只是用到一种算法,就使用缺省参数(参见条款38)。否则,就使用函数重载。</p> 
<p>下面是一个最多可以计算五个int的最大值的函数。这个函数使用了——深呼一口气,看清楚啦——std::numeric_limits&lt;int&gt;::min(),作为缺省参数值。等会儿再进一步介绍这个值,这里先给出函数的代码:</p> 
<p>int max(int a,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b = std::numeric_limits&lt;int&gt;::min(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int c = std::numeric_limits&lt;int&gt;::min(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int d = std::numeric_limits&lt;int&gt;::min(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int e = std::numeric_limits&lt;int&gt;::min())<br>{<br>&nbsp; int temp = a &gt; b ? a : b;<br>&nbsp; temp = temp &gt; c ? temp : c;<br>&nbsp; temp = temp &gt; d ? temp : d;<br>&nbsp; return temp &gt; e ? temp : e;<br>}</p> 
<p>现在可以放松了。std::numeric_limits&lt;int&gt;::min()是c++标准库用一种特有的新方法所表示的一个在c里已经定义了的东西,即c在&lt;limits.h&gt;中定义的int_min宏所表示的那个东西——处理你的c++原代码的编译器所产生的int的最小可能值。是的,它的句法背离了c所具有的简洁,但在那些冒号以及其它奇怪的句法背后,是有道理可循的。</p> 
<p>假设想写一个函数模板,其参数为固定数字类型,模板产生的函数可以打印用“实例化类型”表示的最小值。这个模板可以这么写:</p> 
<p>template&lt;class t&gt;<br>void printminimumvalue()<br>{<br>&nbsp; cout &lt;&lt; 表示为t类型的最小值;<br>}</p> 
<p>如果只是借助&lt;limits.h&gt;和&lt;float.h&gt;来写这个函数会觉得很困难,因为不知道t是什么,所以不知道该打印int_min还是dbl_min,或其它什么类型的值。</p> 
<p>为避开这些困难,标准c++库(见条款49)在头文件&lt;limits&gt; 中定义了一个类模板numeric_limits,这个类模板本身也定义了一些静态成员函数。每个函数返回的是“实例化这个模板的类型”的信息。也就是说,numeric_limits&lt;int&gt;中的函数返回的信息是关于类型int的,numeric_limits&lt;double&gt; 中的函数返回的信息是关于类型double的。numeric_limits中有一个函数叫min,min返回可表示为“实例化类型”的最小值,所以numeric_limits&lt;int&gt;::min()返回的是代表整数类型的最小值。</p> 
<p>有了numeric_limits(和标准库中其它东西一样,numeric_limits存在于名字空间std中;numeric_limits本身在头文件&lt;limits&gt;中),写printminimumvalue就可以象下面这样容易:</p> 
<p>template&lt;class t&gt;<br>void printminimumvalue()<br>{<br>&nbsp; cout &lt;&lt; std::numeric_limits&lt;t&gt;::min();<br>}</p> 
<p>采用基于numeric_limits的方法来表示“类型相关常量”看起来开销很大,其实不然。因为原代码的冗长的语句不会反映到生成的目标代码中。实际上,对numeric_limits的调用根本就不产生任何指令。想知道怎么回事,看看下面,这是numeric_limits&lt;int&gt;::min的一个很简单的实现:</p> 
<p>#include &lt;limits.h&gt;</p> 
<p>namespace std {</p> 
<p>&nbsp; inline int numeric_limits&lt;int&gt;::min() throw ()<br>&nbsp; { return int_min; }</p> 
<p>}</p> 
<p>因为此函数声明为inline,对它的调用会被函数体代替(见条款33)。它只是个int_min,也就是说,它本身仅仅是个简单的“实现时定义的常量”的#define。所以即使本条款开头的那个max函数看起来好象对每个缺省参数进行了函数调用,其实只不过是用了另一种聪明的方法来表示一个类型相关常量而已(本例中常量值为int_min)。象这样一些高效巧妙的应用在c++标准库里俯拾皆是,这可以参考条款49。</p> 
<p>回到max 函数上来:最关键的一点是,不管函数的调用者提供几个参数,max计算时采用的是相同(效率很低)的算法。在函数内部任何地方都不用在意哪些参数是“真”的,哪些是缺省值;而且,所选用的缺省值不可能影响到所采用的算法计算的正确性。这就是使用缺省参数值的方案可行的原因。</p> 
<p>对很多函数来说,会找不到合适的缺省值。例如,假设想写一个函数来计算最多可达5个int的平均值。这里就不能用缺省参数,因为函数的结果取决于传入的参数的个数:如果传入3个值,就要将总数除以3;如果传入5个值,就要将总数除以5。另外,假如用户没有提供某个参数时,没有一个“神奇的数字”可以作为缺省值,因为所有可能的int都可以是有效参数。这种情况下就别无选择:必须重载函数:</p> 
<p>double avg(int a);<br>double avg(int a, int b);<br>double avg(int a, int b, int c);<br>double avg(int a, int b, int c, int d);<br>double avg(int a, int b, int c, int d, int e);</p> 
<p>另一种必须使用重载函数的情况是:想完成一项特殊的任务,但算法取决于给定的输入值。这种情况对于构造函数很常见:“缺省”构造函数是凭空(没有输入)构造一个对象,而拷贝构造函数是根据一个已存在的对象构造一个对象:</p> 
<p>// 一个表示自然数的类<br>class natural {<br>public:<br>&nbsp; natural(int initvalue);<br>&nbsp; natural(const natural&amp; rhs);</p> 
<p>private:<br>&nbsp; unsigned int value;</p> 
<p>&nbsp; void init(int initvalue);<br>&nbsp; void error(const string&amp; msg);<br>};</p> 
<p>inline<br>void natural::init(int initvalue) { value = initvalue; }</p> 
<p>natural::natural(int initvalue)<br>{<br>&nbsp; if (initvalue &gt; 0) init(initvalue);<br>&nbsp; else error("illegal initial value");<br>}</p> 
<p>inline natural::natural(const natural&amp; x)<br>{ init(x.value); }</p> 
<p>输入为int的构造函数必须执行错误检查,而拷贝构造函数不需要,所以需要两个不同的函数来实现,这就是重载。还请注意,两个函数都必须对新对象赋一个初值。这会导致在两个构造函数里出现重复代码,所以要写一个“包含有两个构造函数公共代码”的私有成员函数init来解决这个问题。这个方法——在重载函数中调用一个“为重载函数完成某些功能”的公共的底层函数——很值得牢记,因为它经常有用(见条款12)。<br> 
</p> 
</DIV></div></div> 
 
</center></BODY></HTML> 

⌨️ 快捷键说明

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