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

📄 mi27.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<A NAME="60018"></A>
  static void *operator new(size_t size);
  static void operator delete(void *ptr);
<A NAME="60020"></A>
  bool isOnHeap() const;
<A NAME="60021"></A>
private:
  typedef const void* RawAddress;
  static list&lt;RawAddress&gt; addresses;
};
</PRE>
</UL><A NAME="60027"></A>

<P><A NAME="dingp50"></A>This class uses the <CODE>list</CODE> data structure that's part of the standard C++ library (see <a href="../EC/EI49_FR.HTM#8392" TARGET="_top">Item E49</A> and <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A>) to keep track of all pointers returned from <CODE>operator</CODE> <CODE>new</CODE>. That function allocates memory and adds entries to the list; <CODE>operator</CODE> <CODE>delete</CODE> deallocates memory and removes entries from the list; and <CODE>isOnHeap</CODE> returns whether an object's address is in the <NOBR>list.<SCRIPT>create_link(50);</SCRIPT>
</NOBR></P>
<A NAME="52391"></A>

<P><A NAME="dingp51"></A>Implementation of the <CODE>HeapTracked</CODE> class is simple, because the global <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> functions are called to perform the real memory allocation and deallocation, and the <CODE>list</CODE> class has functions to make insertion, removal, and lookup single-statement operations. Here's the full implementation of <CODE>HeapTracked</CODE>:<SCRIPT>create_link(51);</SCRIPT>
</P>

<A NAME="60053"></A>
<UL><PRE>// mandatory definition of static class member
list&lt;RawAddress&gt; HeapTracked::addresses;
<A NAME="60054"></A>
<A NAME="p155"></A>// HeapTracked's destructor is pure virtual to make the
// class abstract (see <A HREF="../EC/EI14_FR.HTM#223029" TARGET="_top">Item E14</A>). The destructor must still
// be defined, however, so we provide this empty definition.
HeapTracked::~HeapTracked() {}
<A NAME="60055"></A>

<A NAME="60310"></A>
void * HeapTracked::operator new(size_t size)
{
  void *memPtr = ::operator new(size);  // get the memory
<A NAME="60058"></A>
  addresses.push_front(memPtr);         // put its address at
                                        // the front of the list
  return memPtr;
}
<A NAME="60061"></A>
void HeapTracked::operator delete(void *ptr)
{
  // get an "iterator" that identifies the list
  // entry containing ptr; see <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A> for details
  list&lt;RawAddress&gt;::iterator it =
    find(addresses.begin(), addresses.end(), ptr);
<A NAME="60064"></A>
  if (it != addresses.end()) {       // if an entry was found
    addresses.erase(it);             // remove the entry
    ::operator delete(ptr);          // deallocate the memory
  } else {                           // otherwise
    throw MissingAddress();          // ptr wasn't allocated by
  }                                  // op. new, so throw an
}                                    // exception
<A NAME="60071"></A>
bool HeapTracked::isOnHeap() const
{
  // get a pointer to the beginning of the memory
  // occupied by *this; see below for details
  const void *rawAddress = dynamic_cast&lt;const void*&gt;(this);
<A NAME="60074"></A>
  // look up the pointer in the list of addresses
  // returned by operator new
  list&lt;RawAddress&gt;::iterator it =
    find(addresses.begin(), addresses.end(), rawAddress);
<A NAME="60075"></A>
  return it != addresses.end();      // return whether it was
}                                    // found
</PRE>
</UL><A NAME="22833"></A>

<P><A NAME="dingp52"></A>This code is straightforward, though it may not look that way if you are unfamiliar with the <CODE>list</CODE> class and the other components of the Standard Template Library. <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A> explains everything, but the comments in the code above should be sufficient to explain what's happening in this <NOBR>example.<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>
<A NAME="60395"></A>

<P><A NAME="dingp53"></A>The only other thing that may confound you is this statement (in <CODE>isOnHeap</CODE>):<SCRIPT>create_link(53);</SCRIPT>
</P>
<A NAME="60368"></A>

<UL><PRE>const void *rawAddress = dynamic_cast&lt;const void*&gt;(this);
</PRE>
</UL><A NAME="60186"></A>

<P><A NAME="dingp54"></A><A NAME="p156"></A>I mentioned earlier that writing the global function <CODE>isSafeToDelete</CODE> is complicated by the fact that objects with multiple or virtual base classes have several addresses. That problem plagues us in <CODE>isOnHeap</CODE>, too, but because <CODE>isOnHeap</CODE> applies only to <CODE>HeapTracked</CODE> objects, we can exploit a special feature of the <CODE>dynamic_cast</CODE> operator (see <a href="./MI2_FR.HTM#77216" TARGET="_top">Item 2</A>) to eliminate the problem. Simply put, <CODE>dynamic_cast</CODE>ing a pointer to <CODE>void*</CODE> (or <CODE>const</CODE> <CODE>void*</CODE> or <CODE>volatile</CODE> <CODE>void*</CODE> or, for those who can't get enough modifiers in their usual diet, <CODE>const</CODE> <CODE>volatile</CODE> <CODE>void*</CODE>) yields a pointer to the beginning of the memory for the object pointed to by the pointer. But <CODE>dynamic_cast</CODE> is applicable only to pointers to objects that have at least one virtual function. Our ill-fated <CODE>isSafeToDelete</CODE> function had to work with <I>any</I> type of pointer, so <CODE>dynamic_cast</CODE> wouldn't help it. <CODE>isOnHeap</CODE> is more selective (it tests only pointers to <CODE>HeapTracked</CODE> objects), so <CODE>dynamic_cast</CODE>ing <CODE>this</CODE> to <CODE>const</CODE> <CODE>void*</CODE> gives us a pointer to the beginning of the memory for the current object. That's the pointer that <CODE>HeapTracked</CODE>::<CODE>operator</CODE> <CODE>new</CODE> must have returned if the memory for the current object was allocated by <CODE>HeapTracked</CODE>::<CODE>operator</CODE> <CODE>new</CODE> in the first place. Provided your compilers support the <CODE>dynamic_cast</CODE> operator, this technique is completely <NOBR>portable.<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P><A NAME="60184"></A>

<P><A NAME="dingp55"></A>Given this class, even BASIC programmers could add to a class the ability to track pointers to heap allocations. All they'd need to do is have the class inherit from <CODE>HeapTracked</CODE>. If, for example, we want to be able to determine whether a pointer to an <CODE>Asset</CODE> object points to a heap-based object, we'd modify <CODE>Asset</CODE>'s class definition to specify <CODE>HeapTracked</CODE> as a base <NOBR>class:<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>
<A NAME="22995"></A>
<UL><PRE>class Asset: public HeapTracked {
private:
  UPNumber value;
  ...
</PRE>
</UL><A NAME="60193"></A>
<UL><PRE>};
</PRE>
</UL>

<A NAME="23000"></A>
<P><A NAME="dingp56"></A>We could then query <CODE>Asset*</CODE> pointers as <NOBR>follows:<SCRIPT>create_link(56);</SCRIPT>
</NOBR></P>
<A NAME="23001"></A>
<UL><PRE>void inventoryAsset(const Asset *ap)
{
  if (ap-&gt;isOnHeap()) {
    <I>ap is a heap-based asset &#151; inventory it as such;</I>
  }
  else {
    <I>ap is a non-heap-based asset &#151; record it that way;</I>
  }
}
</PRE>
</UL><A NAME="23145"></A>

<P><A NAME="dingp57"></A>A disadvantage of a mixin class like <CODE>HeapTracked</CODE> is that it can't be used with the built-in types, because types like <CODE>int</CODE> and <CODE>char</CODE> can't in<A NAME="p157"></A>herit from anything. Still, the most common reason for wanting to use a class like <CODE>HeapTracked</CODE> is to determine whether it's okay to "<CODE>delete</CODE> <CODE>this</CODE>," and you'll never want to do that with a built-in type because such types have no <CODE>this</CODE> <NOBR>pointer.<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp58"></A><font ID="mhtitle">Prohibiting Heap-Based Objects</font><SCRIPT>create_link(58);</SCRIPT>
</P>

<A NAME="60440"></A>

<P><A NAME="dingp59"></A>Thus ends our examination of determining whether an object is on the heap. At the opposite end of the spectrum is <I>preventing</I> objects from being allocated on the heap. Here the outlook is a bit brighter. There are, as usual, three cases: objects that are directly instantiated, objects instantiated as base class parts of derived class objects, and objects embedded inside other objects. We'll consider each in <NOBR>turn.<SCRIPT>create_link(59);</SCRIPT>
</NOBR></P>
<A NAME="22416"></A>

<P><A NAME="dingp60"></A>Preventing clients from directly instantiating objects on the heap is easy, because such objects are always created by calls to <CODE>new</CODE> and you can make it impossible for clients to call <CODE>new</CODE>. Now, you can't affect the availability of the <CODE>new</CODE> operator (that's built into the language), but you can take advantage of the fact that the <CODE>new</CODE> operator always calls <CODE>operator</CODE> <CODE>new</CODE> (see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>), and that function is one you can declare yourself. In particular, it is one you can declare <CODE>private</CODE>. If, for example, you want to keep clients from creating <CODE>UPNumber</CODE> objects on the heap, you could do it this <NOBR>way:<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P>
<A NAME="22457"></A>
<UL><PRE>class UPNumber {
private:
  static void *operator new(size_t size);
  static void operator delete(void *ptr);
  ...
};
</PRE>
</UL><A NAME="22441"></A>

<P><A NAME="dingp61"></A>Clients can now do only what they're supposed to be able to <NOBR>do:<SCRIPT>create_link(61);</SCRIPT>
</NOBR></P>

<A NAME="22442"></A>
<UL><PRE>UPNumber n1;                         // okay
<A NAME="22443"></A>
static UPNumber n2;                  // also okay
<A NAME="22444"></A>
UPNumber *p = new UPNumber;          // error! attempt to call
                                     // private operator new
</PRE>
</UL>
<A NAME="22485"></A>

<P><A NAME="dingp62"></A>It suffices to declare <CODE>operator</CODE> <CODE>new</CODE> private, but it looks strange to have <CODE>operator</CODE> <CODE>new</CODE> be private and <CODE>operator</CODE> <CODE>delete</CODE> be public, so unless there's a compelling reason to split up the pair, it's best to declare them in the same part of a class. If you'd like to prohibit heap-based arrays of <CODE>UPNumber</CODE> objects, too, you could declare <CODE>operator</CODE> <CODE><NOBR>new[]</NOBR></CODE> and <CODE>operator</CODE> <CODE><NOBR>delete[]</NOBR></CODE> (see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>) private as well. (The bond between <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> is stronger than many people think. For information on a rarely-understood aspect of their relationship, turn to <A HREF="../MAGAZINE/CO_FRAME.HTM#sidebar" TARGET="_top">the sidebar</A> in <A HREF="../MAGAZINE/CO_FRAME.HTM" TARGET="_top">my article on counting objects</A>.)<SCRIPT>create_link(62);</SCRIPT>
</P>
<A NAME="22447"></A>

<P><A NAME="dingp63"></A>Interestingly, declaring <CODE>operator</CODE> <CODE>new</CODE> private often also prevents <CODE>UPNumber</CODE> objects from being instantiated as base class parts of heap-<A NAME="p158"></A>based derived class objects. That's because <CODE>operator</CODE> <CODE>new</CODE> and <CODE>operator</CODE> <CODE>delete</CODE> are inherited, so if these functions aren't declared public in a derived class, that class inherits the private versions declared in its <NOBR>base(s):<SCRIPT>create_link(63);</SCRIPT>
</NOBR></P>

<A NAME="22480"></A>
<UL><PRE>class UPNumber { ... };             // as above
<A NAME="22481"></A>
class NonNegativeUPNumber:          // assume this class
  public UPNumber {                 // declares no operator new
  ...
};
<A NAME="22505"></A>
NonNegativeUPNumber n1;             // okay
<A NAME="22506"></A>
static NonNegativeUPNumber n2;      // also okay
<A NAME="22534"></A>
NonNegativeUPNumber *p =            // error! attempt to call
  new NonNegativeUPNumber;          // private operator new
</PRE>
</UL>
<A NAME="22538"></A>

<P><A NAME="dingp64"></A>If the derived class declares an <CODE>operator</CODE> <CODE>new</CODE> of its own, that function will be called when allocating derived class objects on the heap, and a different way will have to be found to prevent <CODE>UPNumber</CODE> base class parts from winding up there. Similarly, the fact that <CODE>UPNumber</CODE>'s <CODE>operator</CODE> <CODE>new</CODE> is private has no effect on attempts to allocate objects containing <CODE>UPNumber</CODE> objects as <NOBR>members:<SCRIPT>create_link(64);</SCRIPT>
</NOBR></P>

<A NAME="22546"></A>
<UL><PRE>class Asset {
public:
  Asset(int initValue);
  ...
<A NAME="57722"></A>
private:
  UPNumber value;
};
<A NAME="22475"></A>
Asset *pa = new Asset(100);          // fine, calls
                                     // Asset::operator new or
                                     // ::operator new, not
                                     // UPNumber::operator new
</PRE>
</UL>
<A NAME="22555"></A>

<P><A NAME="dingp65"></A>For all practical purposes, this brings us back to where we were when we wanted to throw an exception in the <CODE>UPNumber</CODE> constructors if a <CODE>UPNumber</CODE> object was being constructed in memory that wasn't on the heap. This time, of course, we want to throw an exception if the object in question <I>is</I> on the heap. Just as there is no portable way to determine if an address is on the heap, however, there is no portable way to determine that it is not on the heap, so we're out of luck. This should be no surprise. After all, if we could tell when an address <I>is</I> on the heap, we could surely tell when an address is <I>not</I> on the heap. But we can't, so we can't. Oh <NOBR>well.<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI26_FR.HTM" TARGET="_top">Item 26: Limiting the number of objects of a class</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI28_FR.HTM" TARGET="_top">Item 28: Smart pointers</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

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