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

📄 chapter16.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
  <font color=#009900>// Prevent assignment and copy-construction:</font>
  AutoCounter(<font color=#0000ff>const</font> AutoCounter&amp;);
  <font color=#0000ff>void</font> <font color=#0000ff>operator</font>=(<font color=#0000ff>const</font> AutoCounter&amp;);
<font color=#0000ff>public</font>:
  <font color=#009900>// You can only create objects with this:</font>
  <font color=#0000ff>static</font> AutoCounter* create() { 
    <font color=#0000ff>return</font> <font color=#0000ff>new</font> AutoCounter();
  }
  ~AutoCounter() {
    std::cout &lt;&lt; <font color=#004488>"destroying["</font> &lt;&lt; id 
              &lt;&lt; <font color=#004488>"]"</font> &lt;&lt; std::endl;
    verifier.remove(<font color=#0000ff>this</font>);
  }
  <font color=#009900>// Print both objects and pointers:</font>
  <font color=#0000ff>friend</font> std::ostream&amp; <font color=#0000ff>operator</font>&lt;&lt;(
    std::ostream&amp; os, <font color=#0000ff>const</font> AutoCounter&amp; ac){
    <font color=#0000ff>return</font> os &lt;&lt; <font color=#004488>"AutoCounter "</font> &lt;&lt; ac.id;
  }
  <font color=#0000ff>friend</font> std::ostream&amp; <font color=#0000ff>operator</font>&lt;&lt;(
    std::ostream&amp; os, <font color=#0000ff>const</font> AutoCounter* ac){
    <font color=#0000ff>return</font> os &lt;&lt; <font color=#004488>"AutoCounter "</font> &lt;&lt; ac-&gt;id;
  }
}; 
#endif <font color=#009900>// AUTOCOUNTER_H ///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>AutoCounter</B> class does two
things. First, it sequentially numbers each instance of <B>AutoCounter</B>: the
value of this number is kept in <B>id</B>, and the number is generated using the
<B>static</B> data member <B>count</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Second, and more complex, a
<A NAME="Index2669"></A><B>static</B> instance (called <B>verifier</B>) of
the<B> </B>nested class <B>CleanupCheck</B> keeps track of all of the
<B>AutoCounter</B> objects that are created and destroyed, and reports back to
you if you don&#8217;t clean all of them up (i.e. if there is a memory leak).
This behavior is accomplished using a
<A NAME="Index2670"></A><A NAME="Index2671"></A><A NAME="Index2672"></A><B>set</B>
class from the Standard C++ Library, which is a wonderful example of how
well-designed templates can make life easy (you can learn about all the
containers in the Standard C++ Library in Volume 2 of this book, available
online).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>set</B> class is templatized on
the type that it holds; here it is instantiated to hold <B>AutoCounter</B>
pointers. A <B>set</B> will allow only one instance of each distinct object to
be added; in <B>add(&#160;)</B> you can see this take place with the
<B>set::insert(&#160;)</B> function. <B>insert(&#160;)</B> actually informs you
with its return value if you&#8217;re trying to add something that&#8217;s
already been added; however, since object addresses are being added we can rely
on C++&#8217;s guarantee that all objects have unique
addresses.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>remove(&#160;)</B>,
<B>set::erase(&#160;)</B> is used to remove an <B>AutoCounter</B> pointer from
the <B>set</B>. The return value tells you how many instances of the element
were removed; in our case we only expect zero or one. If the value is zero,
however, it means this object was already deleted from the <B>set</B> and
you&#8217;re trying to delete it a second time, which is a programming error
that will be reported through
<A NAME="Index2673"></A><B>require(&#160;)</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The destructor for <B>CleanupCheck</B>
does a final check by making sure that the size of the <B>set</B> is zero
&#8211; this means that all of the objects have been properly cleaned up. If
it&#8217;s not zero, you have a memory leak, which is reported through
<B>require(&#160;)</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The constructor and destructor for
<B>AutoCounter</B> register and unregister themselves with the <B>verifier</B>
object. Notice that the constructor, copy-constructor, and assignment operator
are <B>private</B>, so the only way for you to create an object is with the
<B>static create(&#160;)</B> member function &#8211; this is a simple example of
a <A NAME="Index2674"></A><I>factory</I>, and it
<A NAME="Index2675"></A>guarantees that all objects are created on the heap, so
<B>verifier</B> will not get confused over assignments and
copy-constructions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since all of the member functions have
been inlined, the only reason for the implementation file is to contain the
static data member definitions:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:AutoCounter.cpp {O}</font>
<font color=#009900>// Definition of static class members</font>
#include <font color=#004488>"AutoCounter.h"</font>
AutoCounter::CleanupCheck AutoCounter::verifier;
<font color=#0000ff>int</font> AutoCounter::count = 0;
<font color=#009900>///:~ </font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">With <B>AutoCounter</B> in hand, we can
now test the facilities of the <B>PStash</B>. The following example not only
shows that the <B>PStash </B>destructor cleans up all the objects that it
currently owns, but it also demonstrates how the <B>AutoCounter</B> class
detects objects that haven&#8217;t been cleaned up:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:TPStashTest.cpp</font>
<font color=#009900>//{L} AutoCounter</font>
#include <font color=#004488>"AutoCounter.h"</font>
#include <font color=#004488>"TPStash.h"</font>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;

<font color=#0000ff>int</font> main() {
  PStash&lt;AutoCounter&gt; acStash;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; 10; i++)
    acStash.add(AutoCounter::create());
  cout &lt;&lt; <font color=#004488>"Removing 5 manually:"</font> &lt;&lt; endl;
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 0; j &lt; 5; j++)
    <font color=#0000ff>delete</font> acStash.remove(j);
  cout &lt;&lt; <font color=#004488>"Remove two without deleting them:"</font>
       &lt;&lt; endl;
  <font color=#009900>// ... to generate the cleanup error message.</font>
  cout &lt;&lt; acStash.remove(5) &lt;&lt; endl;
  cout &lt;&lt; acStash.remove(6) &lt;&lt; endl;
  cout &lt;&lt; <font color=#004488>"The destructor cleans up the rest:"</font>
       &lt;&lt; endl;
  <font color=#009900>// Repeat the test from earlier chapters: </font>
  ifstream in(<font color=#004488>"TPStashTest.cpp"</font>);
  assure(in, <font color=#004488>"TPStashTest.cpp"</font>);
  PStash&lt;string&gt; stringStash;
  string line;
  <font color=#0000ff>while</font>(getline(in, line))
    stringStash.add(<font color=#0000ff>new</font> string(line));
  <font color=#009900>// Print out the strings:</font>
  <font color=#0000ff>for</font>(<font color=#0000ff>int</font> u = 0; stringStash[u]; u++)
    cout &lt;&lt; <font color=#004488>"stringStash["</font> &lt;&lt; u &lt;&lt; <font color=#004488>"] = "</font>
         &lt;&lt; *stringStash[u] &lt;&lt; endl;
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When <B>AutoCounter </B>elements 5 and 6
are removed from the <B>PStash</B>, they become the responsibility of the
caller, but since the caller never cleans them up they cause memory leaks, which
are then detected by <B>AutoCounter</B> at run time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you run the program, you&#8217;ll
see that the error message isn&#8217;t as specific as it could be. If you use
the scheme presented in <B>AutoCounter</B> to discover memory leaks in your own
system, you will probably want to have it print out more detailed information
about the objects that haven&#8217;t been cleaned up. Volume 2 of this book
shows more sophisticated ways to do
this.</FONT><A NAME="_Toc472655057"></A><BR></P></DIV>
<A NAME="Heading476"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Turning ownership on and off</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Let&#8217;s return to the ownership
problem<A NAME="Index2676"></A><A NAME="Index2677"></A>. Containers that hold
objects by value don&#8217;t usually worry about ownership because they clearly
own the objects they contain. But if your container holds pointers (which is
more common with C++, especially with <A NAME="Index2678"></A>polymorphism),
then it&#8217;s very likely those pointers may also be used somewhere else in
the program, and you don&#8217;t necessarily want to delete the object because
then the other pointers in the program would be referencing a destroyed object.
To prevent this from happening, you must consider ownership when designing and
using a container.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Many programs are much simpler than this,
and don&#8217;t encounter the ownership problem: One container holds pointers to
objects that are used only by that container. In this case ownership is very
straightforward: The container owns its objects.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The best approach to handling the
ownership problem is to give the client programmer a choice. This is often
accomplished by a constructor argument that defaults to indicating ownership
(the simplest case). In addition there may be &#8220;get&#8221; and
&#8220;set&#8221; functions to view and modify the ownership of the container.
If the container has functions to remove an object, the ownership state usually
affects that removal, so you may also find options to control destruction in the
removal function. You could conceivably add ownership data for every element in
the container, so each position would know whether it needed to be destroyed;
this is a variant of reference counting, <A NAME="Index2679"></A> except that
the container and not the object knows the number of references pointing to an
object.</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:OwnerStack.h</font>
<font color=#009900>// Stack with runtime conrollable ownership</font>
#ifndef OWNERSTACK_H
#define OWNERSTACK_H

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt; <font color=#0000ff>class</font> Stack {
  <font color=#0000ff>struct</font> Link {
    T* data;
    Link* next;
    Link(T* dat, Link* nxt) 
      : data(dat), next(nxt) {}
  }* head;
  <font color=#0000ff>bool</font> own;
<font color=#0000ff>public</font>:
  Stack(<font color=#0000ff>bool</font> own = <font color=#0000ff>true</font>) : head(0), own(own) {}
  ~Stack();
  <font color=#0000ff>void</font> push(T* dat) {
    head = <font color=#0000ff>new</font> Link(dat,head);
  }
  T* peek() <font color=#0000ff>const</font> { 
    <font color=#0000ff>return</font> head ? head-&gt;data : 0; 
  }
  T* pop();
  <font color=#0000ff>bool</font> owns() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> own; }
  <font color=#0000ff>void</font> owns(<font color=#0000ff>bool</font> newownership) {
    own = newownership;
  }
  <font color=#009900>// Auto-type conversion: true if not empty:</font>
  <font color=#0000ff>operator</font> <font color=#0000ff>bool</font>() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> head != 0; }
};

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt; T* Stack&lt;T&gt;::pop() {
  <font color=#0000ff>if</font>(head == 0) <font color=#0000ff>return</font> 0;
  T* result = head-&gt;data;
  Link* oldHead = head;
  head = head-&gt;next;
  <font color=#0000ff>delete</font> oldHead;
  <font color=#0000ff>return</font> result;
}

<font color=#0000ff>template</font>&lt;<font color=#0000ff>class</font> T&gt; Stack&lt;T&gt;::~Stack() {
  <font color=#0000ff>if</font>(!own) <font color=#0000ff>return</font>;
  <font color=#0000ff>while</font>(head)
    <font color=#0000ff>delete</font> pop();
}
#endif <font color=#009900>// OWNERSTACK_H ///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The default behavior is for the container
to destroy its objects but you can change this by either modifying the
constructor argument or using the <B>owns(&#160;)</B> read/write member
functions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As with most templates you&#8217;re
likely to see, the entire implementation is contained in the header file.
Here&#8217;s a small test that exercises the ownership
abilities:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C16:OwnerStackTest.cpp</font>
<font color=#009900>//{L} AutoCounter </font>
#include <font color=#004488>"AutoCounter.h"</font>
#inclu

⌨️ 快捷键说明

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