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

📄 ch09.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<blockquote>  <hr>  <strong>NOTE: </strong> Whether identical string literals are treated as distinct   objects is implementation-dependent. Some implementations might store the constants   <tt>msg1</tt> and <tt>msg2</tt> at the same memory address (on such implementations,   the expression <tt>bool equal = (v1 == v2);</tt> yields true). However, the   discussion here assumes that <tt>msg1</tt> and <tt>msg2</tt> are stored in two   distinct memory addresses.   <hr></blockquote><p>Although <tt>v1</tt> and <tt>v2</tt> have the same number of elements and their   elements hold the same string value, operator <tt>==</tt> returns <tt>false</tt>   because it compares the addresses of the strings rather than the strings themselves.   You can alter this behavior by defining a specialized version of operator <tt>==</tt>   for type <tt>const char *</tt> exclusively, which compares the strings rather   than their addresses. The compiler picks the specialized version only when objects   of type <tt>Vector&lt;const char *&gt;</tt> are compared. Otherwise, it uses   the primary version of operator <tt>==</tt>. It is not necessary to add the   declaration of the specialized friend operator <tt>==</tt> in the declaration   of the template class <tt>Vector</tt>. However, it is still recommended that   you do so in order to document the existence of a specialized operator <tt>==</tt>.   For example</p><pre><tt>template &lt;class T&gt; class Vector;</tt><tt>template &lt;class T&gt; bool operator== (const Vector&lt;T&gt;&amp; v1, const Vector&lt;T&gt;&amp; v2);</tt><tt>template &lt;class T&gt; class Vector   </tt><tt>{</tt><tt>//...</tt><tt>public:</tt><tt>  friend bool operator==&lt;T&gt; (const Vector&lt;T&gt;&amp; v1, </tt><tt>                             const Vector&lt;T&gt;&amp; v2); // primary</tt><tt>  friend bool operator== ( //specialized version </tt><tt>                                 const Vector&lt;const char *&gt;&amp; v1,</tt><tt>                                 const Vector&lt;const char *&gt;&amp; v2);</tt><tt>};</tt></pre><p>The definition of the specialized function must appear after the generic version.   Therefore, you place it at the same header file, right after the generic version.   Following is the specialized version:</p><pre><tt>//appended to vector.hpp</tt><tt>#include &lt;cstring&gt; //needed for strcmp</tt><tt>using namespace std;</tt><tt>template &lt;&gt; bool operator== (</tt><tt>                                            const Vector&lt;const char *&gt;&amp; v1,</tt><tt>                                            const Vector&lt;const char *&gt;&amp; v2 )</tt><tt>{</tt><tt>  if (v1.size() != v2.size())  //as before</tt><tt>    return false;</tt><tt>  for (size_t i = 0; i&lt;v1.size(); i++)</tt><tt>  {</tt><tt>    if (strcmp(v1[i], v2[i])  != 0) //compare string values</tt><tt>      return false;</tt><tt>  }</tt><tt>  return true;</tt><tt>}</tt></pre><p>Here again, the empty angular brackets that follow the keyword <tt>template</tt>   indicate a specialized version that overrides a previously defined generic version.   The compiler now uses the specialized form of operator <tt>==</tt> to compare   <tt>v1</tt> and <tt>v2</tt>; as expected, the result is now <tt>true</tt>.</p><p>Specialized functions operate in a way that resembles the behavior of virtual   member functions in a derived class. In both cases, the actual function that   is being called depends on the type. However, the virtual dispatch mechanism   relies on the dynamic type of the object, whereas template function specializations   are resolved statically.</p><h2> <a name="Heading12">Function Templates</a></h2><p>Many algorithms perform a sequence of identical operations, regardless of the   data type they manipulate. <tt>min</tt> and <tt>max</tt>, <tt>array sort</tt>,   and <tt>swap</tt> are examples of such type-independent algorithms. In the pre-template   era, programmers had to use alternative techniques to implement generic algorithms:--   macros, <tt>void</tt> pointers, and a common root base -- all of which incurred   significant drawbacks. This section exemplifies the drawbacks of these techniques   and then demonstrates how function templates are used in implementing generic   algorithms. </p><h3> <a name="Heading13">Function-Like Macros</a></h3><p>Function-like macros were the predominant form of implementing generic algorithms   in C. For example</p><pre><tt>#define min(x,y)  ((x)&lt;(y))?(x):(y)</tt><tt>void f()</tt><tt>{</tt><tt>  double dlower = min(5.5, 5.4);  </tt><tt>  int ilower = min(sizeof(double), sizeof(int));  </tt><tt>  char clower = min('a', 'b');  </tt><tt>}</tt></pre><p>The C Standard library defines various function-like macros. To some extent,   they can be used effectively because they avoid the overhead that is associated   with a full-blown function call. However, macros have significant drawbacks   as well. The preprocessor macro expansion is a simple text substitution, with   very limited awareness of scope rules and type-checking. Furthermore, macros   are notoriously hard to debug because the compiler scans a source file that   might look very different from the original file. For this reason, compiler   diagnostics can refer to code that the original source file does not contain.   Macros can easily bloat the size of a program because every macro call is expanded   inline. When large macros are called repeatedly, the result can be a dramatic   increase in the size of the program. In spite of the syntactic similarity, macros   are semantically very different from functions -- they have no linkage, address,   or storage type. For these reasons, use macros sparingly -- if at all.</p><h3> <a name="Heading14">void Pointers</a></h3><p>An alternative approach to macros is the use of a generic pointer, <tt>void   *</tt>, which can hold the address of any data type. The C standard library   defined two generic functions that rely on this feature: <tt>qsort</tt> and   <tt>bsearch</tt>. <tt>qsort</tt> is declared in the header <tt>&lt;stdlib.h&gt;</tt>   as follows:</p><pre><tt>void qsort( void *,</tt><tt>            size_t,</tt><tt>            size_t,</tt><tt>            int (*) (const void *, const void *)</tt><tt>          );</tt></pre><p>The type-unawareness of these generic functions is achieved by using <tt>void</tt>   pointers and an abstract, user-defined comparison function. Still, there are   noticeable limitations to this technique. <tt>void</tt> pointers are not type-safe,   and the repeated function callbacks impose runtime overhead that, in most cases,   cannot be avoided by inlining.</p><h3> <a name="Heading15">A Common Root Base</a></h3><p>In some other object-oriented languages, every object is ultimately derived   from a common base class (this design approach and its deficiencies in C++ are   described in further detail in Chapter 5, "Object-Oriented Programming and Design").   Generic algorithms can rely on this feature. For example</p><pre><tt>// pseudo C++ code</tt><tt>class Object // a common root class</tt><tt>{</tt><tt>public:</tt><tt>  virtual bool operator &lt; (const Object&amp;) const; //polymorphic behavior</tt><tt>  //..other members</tt><tt>};</tt><tt>const Object&amp; min(const Object &amp;x, const Object&amp; y)</tt><tt>{</tt><tt>  return  x.operator&lt;(y) ? x : y; //x and y can be objects of any class type</tt><tt>}</tt></pre><p>Imitating this approach in C++ is not as useful as it is in other languages,   though. C++ does not force a common root class. Therefore, it is the programmer's   -- not the implementation's -- responsibility to ensure that every class is   derived from a common base. Worse yet, the common root class is not standardized.   Thus, such algorithms are not portable. In addition, these algorithms cannot   handle fundamental types because they are limited to class objects exclusively.   Finally, the extensive use of runtime type checking imposes an unacceptable   performance on general-purpose algorithms.</p><h2> </h2><p>Function templates are free from all these drawbacks. They are type-safe, they   can be inlined by the compiler to boost performance, and -- most importantly   -- they are applicable to fundamental types and user-defined types alike.</p><p>A function template declaration contains the keyword <tt>template</tt>, followed   by a list of template parameters and a function declaration. As opposed to ordinary   functions, which are usually declared in one translation unit and defined in   another, the definition of a function template follows its declaration. For   example</p><pre><tt>template &lt;class T&gt; T max( T t1, T t2)</tt><tt>{</tt><tt>  return (t1 &gt; t2) ? t1 : t2;</tt><tt>}</tt></pre><p>Unlike class templates, function template parameters are implicitly deduced   from the type of their arguments. </p><p>In the following example, the compiler instantiates three distinct specializations   of <tt>swap</tt>, according to the type of arguments used in each invocation:</p><pre><tt>#include &lt;string&gt;</tt><tt>using namespace std;</tt><tt>int main()</tt><tt>{</tt><tt>  int i = 0, j = 8;</tt><tt>  char c = 'a', d = 'z';</tt><tt>  string s1 = "first", s2 = "second";</tt><tt>  int nmax = max(i, j);    // int max (int, int);</tt><tt>  char cmax = max(c, d);    // char max (char, char);</tt><tt>  string smax = max(s1, s2);    // string max (string, string);</tt><tt>  return 0;</tt><tt>}</tt></pre><p>It is possible to define several function templates with the same name (that   is, to overload it) or to combine ordinary functions with function templates   that have the same name. For example</p><pre><tt>template &lt;class T&gt; T max( T t1, T t2)</tt><tt>{</tt><tt>  return (t1 &gt; t2) ? t1 : t2;</tt><tt>}</tt><tt>int max (int i, int j)</tt><tt>{</tt><tt>  return (i &gt; j) ? i : j;</tt><tt>}</tt></pre><p>The compiler does not generate an <tt>int</tt> version of <tt>max()</tt>. Instead,   it invokes the function <tt>int max (int, int)</tt> when <tt>max()</tt> is called   with arguments of type <tt>int</tt>.</p><h2> <a name="Heading16">Performance Considerations</a></h2><p>C++ provides several facilities for controlling the instantiation of templates,   including explicit instantiation of templates and exported templates. The following   sections demonstrate how to use these features, as well as other techniques   to enhance performance. </p><h3> <a name="Heading17">Type Equivalence</a></h3><p>Two templates are equivalent (that is, they refer to the same template id)   when all the following conditions hold:</p><ul>  <li>    <p> <b>Name equivalence</b> -- The names of the templates are identical and       they refer to the same template.</p>  </li>  <p></p>  <li>    <p> <b>Argument Equivalence</b> -- The type arguments of the templates are       the same.</p>  </li>  <p></p>  <li>    <p> <b>Identical non-Type Arguments</b> -- The non-type arguments of integral       or enumeration type of both templates have identical values, and their non-type       arguments of pointer or reference type refer to the same external object       or function.</p>  </li>  <p></p>  <li>    <p> <b>Template-template Arguments</b> -- The template-template arguments       of both templates refer to the same template.</p>  </li></ul><p></p><p>Following are some examples:</p><pre><tt>template&lt;class T, long size&gt; class Array</tt><tt>{ /* ... */ };</tt><tt>void func()</tt><tt>{</tt><tt>Array&lt;char, 2*512&gt; a;</tt><tt>Array&lt;char, 1024&gt; b;</tt><tt>}</tt></pre><p>The compiler evaluates constant expressions such as <tt>2*512</tt>, so templates   <tt>a</tt> and <tt>b</tt> are of the same type. Following is another example:</p><pre><tt>template&lt;class T, int(*error_code_fct)()&gt; class Buffer</tt><tt>{ /* ... */ };</tt><tt>int error_handler();</tt><tt>int another_error_handler();</tt><tt>void func()</tt><tt>{</tt><tt>  Buffer&lt;int, &amp;error_handler&gt; b1;</tt><tt>  Buffer&lt;int, &amp;another_error_handler&gt; b2;</tt><tt>  Buffer&lt;int, &amp;another_error_handler&gt; b3;</tt><tt>  Buffer&lt;unsigned int, &amp;another_error_handler&gt; b4;</tt><tt>}</tt></pre><p><tt>b2</tt> and <tt>b3</tt> are of the same type because they are instances   of the same template and they take the same type and non-type arguments. Conversely,   <tt>b1</tt> and <tt>b4</tt> are of distinct types.</p><p>The function <tt>func()</tt> instantiated three distinct specializations from   the same class template: one for <tt>b1</tt>, a second for <tt>b2</tt> and <tt>b3</tt>,   and a third for <tt>b4</tt>. Unlike ordinary function overloading, the compiler   generates a distinct class from the template for every unique type. On machines   that have the same underlying representation for <tt>long</tt> and <tt>int</tt>   types, the following code still results in the instantiation of two distinct   templates:</p><pre><tt>void too_prolific()</tt><tt>{</tt><tt>  Buffer&lt;int, &amp;error_handler&gt; buff1;</tt><tt>  Buffer&lt;long, &amp;error_handler&gt; buff2;</tt><tt>}</tt></pre><p>Similarly, <tt>char</tt> and <tt>unsigned char</tt> are distinct types, even   on machines that treat <tt>char</tt> as <tt>unsigned</tt> by default. Programmers   who are accustomed to the lenient matching rules of ordinary function overloading   are not always aware of the potential code bloat that can result from using   templates with very similar yet distinct types.</p><h3> <a name="Heading18">Avoiding Unnecessary Instantiations</a></h3><p>In the following example, three distinct copies of the template <tt>min</tt>   are generated -- one for each type that is used:</p><pre><tt>template &lt; class T &gt; T min(T f, T s)</tt><tt>{</tt><tt>  return f &lt; s? f: s;</tt><tt>}</tt><tt>void use_min()</tt><tt>{</tt><tt>  int n = 5, m= 10;</tt><tt>  int j = min(n,m);         //min&lt;int&gt; instantiated</tt><tt>  char c = 'a', d = 'b';</tt><tt>  char k = min(c,d);     //min&lt;char&gt; instantiated</tt><tt>  short int u = 5, v = 10;</tt><tt>  short int w = min(u,v);    // min&lt;short int&gt; instantiated</tt><tt>}</tt></pre><p>On the other hand, an ordinary function avoids the automatic generation of   redundant specializations:</p><pre><tt>int min(int f, int s)</tt><tt>{</tt><tt>  return f &lt; s? f: s;</tt><tt>}</tt><tt>void use_min()</tt><tt>{</tt><tt>// all three invocation of min</tt><tt>// use int min (int, int);</tt><tt>  int n = 5, m= 10;</tt><tt>  int j = min(n,m);</tt><tt>  char c = 'a', d = 'b';</tt><tt>  char k = min(c,d); 	</tt>

⌨️ 快捷键说明

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