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

📄 ch26.htm

📁 VC使用所有细节的逻列
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<P>You can, of course, pass as many parameters as you like to a class template, just as you can with a function template. Listing 26.12 shows a class template that uses two different types of data.</P>

<P><I>Listing 26.12&#151;Using Multiple Parameters with a Class Template</I></P>

<pre><font color="#008000">template&lt;class Type1, class Type2&gt;</font></pre>

<pre><font color="#008000">class CMyClass</font></pre>

<pre><font color="#008000">{</font></pre>

<pre><font color="#008000">protected:</font></pre>

<pre><font color="#008000">    Type1 data1;</font></pre>

<pre><font color="#008000">    Type2 data2;</font></pre>

<pre><font color="#008000">public:</font></pre>

<pre><font color="#008000">    CMyClass(Type1 arg1, Type2 arg2)</font></pre>

<pre><font color="#008000">    {</font></pre>

<pre><font color="#008000">        data1 = arg1;</font></pre>

<pre><font color="#008000">        data2 = arg2;</font></pre>

<pre><font color="#008000">    }</font></pre>

<pre><font color="#008000">    ~CMyClass() {}</font></pre>

<pre><font color="#008000">};</font></pre>

<P>To instantiate an object of the <font color="#008000">CMyClass</font> class, you might use a line like this:</P>

<pre><font color="#008000">CMyClass&lt;int, char&gt; myClass(15, 'A');</font></pre>

<P>Finally, you can use specific data types, as well as the placeholder data types, as parameters in a class template. You just add the specific data type to the parameter list, just as you add any other parameter. Listing 26.13 is a short program that 
creates an object from a class template that uses two abstract parameters and one specific data type.</P>

<P><I>Listing 26.13&#151;Using Specific Data Types as Parameters in a Class Template</I></P>

<pre><font color="#008000">#include &lt;iostream.h&gt;</font></pre>

<pre><font color="#008000">template&lt;class Type1, class Type2, int num&gt;</font></pre>

<pre><font color="#008000">class CMyClass</font></pre>

<pre><font color="#008000">{</font></pre>

<pre><font color="#008000">protected:</font></pre>

<pre><font color="#008000">    Type1 data1;</font></pre>

<pre><font color="#008000">    Type2 data2;</font></pre>

<pre><font color="#008000">    int data3;</font></pre>

<pre><font color="#008000">public:</font></pre>

<pre><font color="#008000">    CMyClass(Type1 arg1, Type2 arg2, int num)</font></pre>

<pre><font color="#008000">    {</font></pre>

<pre><font color="#008000"> </font><font color="#008000">data1 = arg1;</font></pre>

<pre><font color="#008000">       data2 = arg2;</font></pre>

<pre><font color="#008000">       data3 = num;</font></pre>

<pre><font color="#008000">    }</font></pre>

<pre><font color="#008000">    ~CMyClass() {}</font></pre>

<pre><font color="#008000">};</font></pre>

<pre><font color="#008000">int main()</font></pre>

<pre><font color="#008000">{</font></pre>

<pre><font color="#008000">    CMyClass&lt;int, char, 0&gt; myClass(15, 'A', 10);</font></pre>

<pre><font color="#008000">    return 0;</font></pre>

<pre><font color="#008000">}</font></pre>

<P><B>The Standard Template Library</B></P>

<P>Before you run off to write templates that implement linked lists, binary trees, sorting, and other common tasks, you might like to know that somebody else already has. Visual C++ incorporates the Standard Template Library (STL), which includes 
hundreds of functon and class templates to tackle common tasks. Would you like a stack of <font color="#008000">int</font>s or a stack of <font color="#008000">float</font>s? Don't write lots of different stack classes, don't even write one stack class 
template, just use the stack template included in the STL. The same is true for almost every common data structure and operation.</P>

<P>Earlier in this chapter you saw applications that use exceptions and allocate memory on the heap (dynamic allocation with new) can run into trouble when exceptions are thrown and the delete statement for that memory gets bypassed. If there was an 
object on the stack whose destructor called delete for the memory, you would prevent this problem. STL implements a managed pointer like this: it's called auto-ptr. Here's the declaration for auto_ptr:</P>

<pre><font color="#008000">template&lt;class T&gt;</font></pre>

<pre><font color="#008000">    class auto_ptr {</font></pre>

<pre><font color="#008000">public:</font></pre>

<pre><font color="#008000">    typedef T element_type;</font></pre>

<pre><font color="#008000">    explicit auto_ptr(T *p = 0) ;</font></pre>

<pre><font color="#008000">    auto_ptr(const auto_ptr&lt;T&gt;&amp; rhs) ;</font></pre>

<pre><font color="#008000">    auto_ptr&lt;T&gt;&amp; operator=(auto_ptr&lt;T&gt;&amp; rhs);</font></pre>

<pre><font color="#008000">    ~auto_ptr();</font></pre>

<pre><font color="#008000">    T&amp; operator*() const ;</font></pre>

<pre><font color="#008000">    T *operator-&gt;() const;</font></pre>

<pre><font color="#008000">    T *get() const ;</font></pre>

<pre><font color="#008000">    T *release() const;</font></pre>

<pre><font color="#008000">    };</font></pre>

<P>Once you create a pointer to an int, float, Employee, or any other type of object, you can make an auto_ptr and use that just like a pointer. For example, imagine a code fragment like this:</P>

<pre><font color="#008000">// ...</font></pre>

<pre><font color="#008000">    Employee* emp = new Employee(stuff);</font></pre>

<pre><font color="#008000">    emp-&gt;ProcessEmployee;</font></pre>

<pre><font color="#008000">    delete emp;</font></pre>

<pre><font color="#008000">// ...</font></pre>

<P>When you realize that <font color="#008000">ProcessEmployee()</font> might throw an <font color="#008000">EmployeeException</font>, you might change this code to read like this:</P>

<pre><font color="#008000">// ...</font></pre>

<pre><font color="#008000">    Employee* emp = new Employee(stuff);</font></pre>

<pre><font color="#008000">    try</font></pre>

<pre><font color="#008000">    {</font></pre>

<pre><font color="#008000">        emp-&gt;ProcessEmployee;</font></pre>

<pre><font color="#008000">    }</font></pre>

<pre><font color="#008000">    catch (EmployeeException e)</font></pre>

<pre><font color="#008000">    {</font></pre>

<pre><font color="#008000">        delete emp;</font></pre>

<pre><font color="#008000">        throw;</font></pre>

<pre><font color="#008000">    }</font></pre>

<pre><font color="#008000">    delete emp;</font></pre>

<pre><font color="#008000">// ...</font></pre>

<P>But you think this is ugly and hard to maintain, so you go with an auto_ptr instead:</P>

<pre><font color="#008000">#include &lt;memory&gt;</font></pre>

<pre><font color="#008000">// ...</font></pre>

<pre><font color="#008000">    auto_ptr&lt;Employee&gt; emp (new Employee(stuff));</font></pre>

<pre><font color="#008000">    emp-&gt;ProcessEmployee;</font></pre>

<pre><font color="#008000">// ...</font></pre>

<P>This looks just like the first example, but it works just like the second: whether you leave this code snippet normally or because of an exception, <font color="#008000">emp</font> will go out of scope, and when it does the Employee object that was 
allocated on the heap will be deleted for you automatically. No extra <font color="#008000">try</font> or <font color="#008000">catch</font> blocks, and as an extra bonus you don't even have to remember to <font color="#008000">delete</font> the memory in 
the routine at all&#151;it's taken care of for you.</P>

<P>Look again at the functions declared in the template: a constructor, a copy constructor, an address-of (<font color="#008000">&amp;</font>) operator, a destructor, a contents of (<font color="#008000">*</font>) operator, a dereferencing (<font 
color="#008000">-&gt;</font>) operator, and functions called <font color="#008000">get()</font> and <font color="#008000">release()</font>. These work together to ensure that you can treat your pointer exactly as though it was an ordinary pointer. It's 
neat stuff.</P>

<H3>Using Run-Time Type Information</H3>

<P>Run-Time Type Information (RTTI) was added to C++ so that programmers could obtain information about objects at runtime. This capability is especially useful when you're dealing with polymorphic objects, because it enables your program to determine at 
runtime what exact type of object it's currently working with. Later in this section, you'll see how important this type of information can be when you're working with class hierarchies. RTTI can also be used to safely downcast an object pointer. In this 
section, you'll discover how RTTI works and why you'd want to use it.</P>

<blockquote><p><img src="tip.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/tip.gif">

<P>Polymorphism is discussed in <A HREF="index04.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index04.htm" target="text">Chapter 4</A>, &quot;Messages and Commands,&quot; in a sidebar in the &quot;Message Maps&quot; section.</P>

<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>

<P><B> Introducing RTTI</B></P>

<P>The RTTI standard introduces three new elements to the C++ language. The <font color="#008000">dynamic_cast</font> operator performs downcasting of polymorphic objects; the <font color="#008000">typeid</font> operator retrieves information (in the form 
of a <font color="#008000">type_info</font> object) about an object; and the <font color="#008000">type_info</font> class stores information about an object, providing member functions that can be used to extract that information.</P>

<P>The public portion of the <font color="#008000">type_info</font> class is defined in Visual C++ and shown in Listing 26.14.</P>

<P><I>Listing 26.14&#151;The </I>type_info<I> Class, defined by Visual C++</I></P>

<pre><font color="#008000">class type_info {</font></pre>

<pre><font color="#008000">public:</font></pre>

<pre><font color="#008000">     virtual ~type_info();</font></pre>

<pre><font color="#008000">     int operator==(const type_info&amp; rhs) const;</font></pre>

<pre><font color="#008000">     int operator!=(const type_info&amp; rhs) const;</font></pre>

<pre><font color="#008000">     int before(const type_info&amp; rhs) const;</font></pre>

<pre><font color="#008000">     const char* name() const;</font></pre>

<pre><font color="#008000">     const char* raw_name() const;</font></pre>

<pre><font color="#008000">};</font></pre>

<P>As you can see, the class provides member functions that can compare objects for equality, and return the object's name, both as a readable text string and as a raw decorated object name. The <font color="#008000">before()</font> member function 
remains a bit mysterious and poorly documented. According to Microsoft, the Visual C++ implementation of <font color="#008000">before()</font> is used &quot;to determine the collating sequence of types.&quot; Microsoft further states that &quot;there is no 
link between the collating order of types and inheritance relationships.&quot;</P>

<P><B>Performing Safe Downcasts</B></P>

<P>Once you start writing a lot of OOP programs, you'll run into times when you need to downcast one type of object to another. <I>Downcasting</I> is the act of converting a base-class pointer to a derived-class pointer (a <I>derived class</I> being a 
class that's derived from the base class). You use <font color="#008000">dynamic_cast</font> to downcast an object, like this:</P>

<P><I><font color="#008000">Type</font></I><font color="#008000">* ptr = dynamic_cast&lt;</font><I><font color="#008000">Type</font></I><font color="#008000">*&gt;(Pointer);</font></pre>

<P>In the preceding example, <font color="#008000">Type</font> is the type to which the object should be cast, and <font color="#008000">Pointer</font> is a pointer to the object. If the pointer cannot be safely downcast, the <font 
color="#008000">dynamic_cast</font> operator returns 0.</P>

<P>Suppose, for example, that you have a base class called <font color="#008000">CBase</font> and a class derived from <font color="#008000">CBase</font> called <font color="#008000">CDerived</font>. Because you want to take advantage of polymorphism, you 
obtained a pointer to <font color="#008000">CDerived</font>, like this:</P>

<pre><font color="#008000">CBase* derived = new CDerived;</font></pre>

<P>Notice that, although you're creating a <font color="#008000">CDerived</font> object, the pointer is of the base-class type, <font color="#008000">CBase</font>. This is a typical scenario in programs that take advantage of OOP polymorphism.</P>

<P>Now suppose that you want to safely downcast the <font color="#008000">CBase</font> pointer to a <font color="#008000">CDerived</font> pointer. You might use <font color="#008000">dynamic_cast</font>, as follows:</P>

<pre><font color="#008000">CDerived* ptr = dynamic_cast&lt;CDerived*&gt;(derived);</font></pre>

<P>If the cast returns 0, the downcast was not allowed.</P>

<P><B>Getting Object Information</B></P>

<P>As mentioned previously, you can use the <font color="#008000">typeid</font> operator to obtain information about an object. Although the <font color="#008000">dynamic_cast</font> operator applies only to polymorphic objects, you can use <font 
color="#008000">typeid</font> on any type of data object. For example, to get information about the <font color="#008000">int</font> data object, you could use lines like these:</P>

<pre><font color="#008000">const type_info&amp; ti = typeid(int);</font></pre>

<pre><font color="#008000">cout &lt;&lt; ti.name();</font></pre>

<P>In the first line, you can see that the <font color="#008000">typeid</font> operator returns a reference to a <font color="#008000">type_info</font> object. You can then use the object's member functions to extract information about the data object. In 
the preceding example

⌨️ 快捷键说明

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