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

📄 mi27.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<TITLE>More Effective C++ | Item 27: Requiring or prohibiting heap-based objects</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 3; setCurrentMax(3);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "MI27_DIR.HTM";
var dingtext = "Item M27, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="M27: Requiring or prohibiting heap-based objects" -->
<A NAME="22627"></A>
<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>


<P><A NAME="dingp1"></A><font ID="mititle">Item 27: &nbsp;Requiring or prohibiting heap-based objects.</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="72177"></A><A NAME="22628"></A>
<P><A NAME="dingp2"></A>Sometimes you want to arrange things so that objects of a particular type can commit suicide, i.e., can "<CODE>delete</CODE> <CODE>this</CODE>." Such an arrangement clearly requires that objects of that type be allocated on the heap. Other times you'll want to bask in the certainty that there can be no memory leaks for a particular class, because none of the objects could have been allocated on the heap. This might be the case if you are working on an embedded system, where memory leaks are especially troublesome and heap space is at a premium. Is it possible to produce code that requires or prohibits heap-based objects? Often it is, but it also turns out that the notion of being "on the heap" is more nebulous than you might <NOBR>think.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp3"></A><font ID="mhtitle">Requiring Heap-Based Objects</font><SCRIPT>create_link(3);</SCRIPT>
</P>

<P><A NAME="dingp4"></A><A NAME="21648"></A>Let us begin with the prospect of limiting object creation to the heap. To enforce such a restriction, you've got to find a way to prevent clients from creating objects other than by calling <CODE>new</CODE>. This is easy to do. Non-heap objects are automatically constructed at their point of definition and automatically destructed at the end of their lifetime, so it suffices to simply make these implicit constructions and destructions <NOBR>illegal.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P>
<A NAME="21654"></A>

<P><A NAME="dingp5"></A>The straightforward way to make these calls illegal is to declare the constructors and the destructor <CODE>private</CODE>. This is overkill. There's no reason why they <I>both</I> need to be private. Better to make the destructor private and the constructors public. Then, in a process that should be familiar from <A HREF="./MI26_FR.HTM#5350" TARGET="_top">Item 26</A>, you can introduce a privileged pseudo-destruc<A NAME="p146"></A>tor function that has access to the real destructor. Clients then call the pseudo-destructor to destroy the objects they've <NOBR>created.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="21709"></A>
<P><A NAME="dingp6"></A>If, for example, we want to ensure that objects representing unlimited precision numbers are created only on the heap, we can do it like <NOBR>this:<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>

<A NAME="37922"></A>
<UL><PRE>class UPNumber {
public:
  UPNumber();
  UPNumber(int initValue);
  UPNumber(double initValue);
  UPNumber(const UPNumber&amp; rhs);
<A NAME="37924"></A>
  // pseudo-destructor (a const member function, because
  // even const objects may be destroyed)
  void destroy() const { delete this; }
<A NAME="37925"></A>
  ...
<A NAME="57667"></A>
private:
  ~UPNumber();
};
</PRE>
</UL>

<A NAME="37939"></A>
<P><A NAME="dingp7"></A>Clients would then program like <NOBR>this:<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="37927"></A>
<UL><PRE>UPNumber n;                          // error! (legal here, but
                                     // illegal when n's dtor is
                                     // later implicitly invoked)
<A NAME="37928"></A>
UPNumber *p = new UPNumber;          // fine
<A NAME="37929"></A>
...
<A NAME="37930"></A>
delete p;                            // error! attempt to call
                                     // private destructor
<A NAME="37931"></A>
p-&gt;destroy();                        // fine
</PRE>
</UL>
<A NAME="37953"></A>
<P><A NAME="dingp8"></A>An alternative is to declare all the constructors private. The drawback to that idea is that a class often has many constructors, and the class's author must remember to declare each of them private. This includes the copy constructor, and it may include a default constructor, too, if these functions would otherwise be generated by compilers; compiler-generated functions are always public (see <A HREF="../EC/EI45_FR.HTM#8160" TARGET="_top">Item E45</A>). As a result, it's easier to declare only the destructor private, because a class can have only one of <NOBR>those.<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P><A NAME="21837"></A>
<P><A NAME="dingp9"></A>Restricting access to a class's destructor or its constructors prevents the creation of non-heap objects, but, in a story that is told in <A HREF="./MI26_FR.HTM#5350" TARGET="_top">Item 26</A>, it also prevents both inheritance and <NOBR>containment:<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="21847"></A>
<UL><PRE>class UPNumber { ... };              // declares dtor or ctors
                                     // private
<A NAME="21848"></A>
class NonNegativeUPNumber:
  public UPNumber { ... };           // error! dtor or ctors
                                     // won't compile
<A NAME="21849"></A>
<A NAME="p147"></A>class Asset {
private:
  UPNumber value;
  ...                                // error! dtor or ctors
                                     // won't compile
};
</PRE>
</UL>
<A NAME="21882"></A>
<P><A NAME="dingp10"></A>Neither of these difficulties is insurmountable. The inheritance problem can be solved by making <CODE>UPNumber</CODE>'s destructor protected (while keeping its constructors public), and classes that need to contain objects of type <CODE>UPNumber</CODE> can be modified to contain <I>pointers</I> to <CODE>UPNumber</CODE> objects <NOBR>instead:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>

<A NAME="21883"></A>
<UL><PRE>class UPNumber { ... };              // declares dtor protected
<A NAME="21884"></A>
class NonNegativeUPNumber:
  public UPNumber { ... };           // now okay; derived
                                     // classes have access to
                                     // protected members
<A NAME="21871"></A>
class Asset {
public:
  Asset(int initValue);
  ~Asset();
  ...
<A NAME="57677"></A>
private:
  UPNumber *value;
};
<A NAME="21895"></A>
Asset::Asset(int initValue)
: value(new UPNumber(initValue))      // fine
{ ... }
<A NAME="21896"></A>
Asset::~Asset()
{ value-&gt;destroy(); }                 // also fine
</PRE>
</UL>

<P><A NAME="dingp11"></A><font ID="mhtitle">Determining Whether an Object is On The Heap</font><SCRIPT>create_link(11);</SCRIPT>
</P>

<P><A NAME="dingp12"></A><A NAME="60442"></A>If we adopt this strategy, we must reexamine what it means to be "on the heap." Given the class definition sketched above, it's legal to define a non-heap <CODE>NonNegativeUPNumber</CODE> <NOBR>object:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="21947"></A>

<UL><PRE>NonNegativeUPNumber n;                // fine
</PRE>
</UL>
<A NAME="21953"></A>
<P><A NAME="dingp13"></A>Now, the <CODE>UPNumber</CODE> part of the <CODE>NonNegativeUPNumber</CODE> object <CODE>n</CODE> is not on the heap. Is that okay? The answer depends on the details of the class's design and implementation, but let us suppose it is <I>not</I> okay, that all <CODE>UPNumber</CODE> objects &#151; even base class parts of more derived objects &#151; <I>must</I> be on the heap. How can we enforce this <NOBR>restriction?<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="21961"></A>

<P><A NAME="dingp14"></A>There is no easy way. It is not possible for a <CODE>UPNumber</CODE> constructor to determine whether it's being invoked as the base class part of a heap-<A NAME="p148"></A>based object. That is, there is no way for the <CODE>UPNumber</CODE> constructor to detect that the following contexts are <NOBR>different:<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>

<A NAME="21984"></A>
<UL><PRE>NonNegativeUPNumber *n1 =
  new NonNegativeUPNumber;            // on heap
<A NAME="21974"></A>
NonNegativeUPNumber n2;               // not on heap
</PRE>
</UL><A NAME="21972"></A>

<P><A NAME="dingp15"></A>But perhaps you don't believe me. Perhaps you think you can play games with the interaction among the <CODE>new</CODE> operator, <CODE>operator</CODE> <CODE>new</CODE> and the constructor that the <CODE>new</CODE> operator calls (see <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A>). Perhaps you think you can outsmart them all by modifying <CODE>UPNumber</CODE> as <NOBR>follows:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="22004"></A>
<UL><PRE>class UPNumber {
public:
  // exception to throw if a non-heap object is created
  class HeapConstraintViolation {};
<A NAME="22005"></A>
  static void * operator new(size_t size);
<A NAME="70822"></A>
  UPNumber();
  ...
<A NAME="57688"></A>
private:
  static bool onTheHeap;                 // inside ctors, whether
                                         // the object being
  ...                                    // constructed is on heap
<A NAME="75713"></A>
};
<A NAME="38011"></A>
// obligatory definition of class static
bool UPNumber::onTheHeap = false;
<A NAME="22045"></A>
void *UPNumber::operator new(size_t size)
{
  onTheHeap = true;
  return ::operator new(size);
}
<A NAME="22002"></A>
UPNumber::UPNumber()
{
  if (!onTheHeap) {
    throw HeapConstraintViolation();
  }
<A NAME="22032"></A>
  <I>proceed with normal construction here;</I>
<A NAME="22033"></A>
  onTheHeap = false;                    // clear flag for next obj.
}
</PRE>
</UL><A NAME="22038"></A>
<P><A NAME="dingp16"></A>There's nothing deep going on here. The idea is to take advantage of the fact that when an object is allocated on the heap, <CODE>operator</CODE> <CODE>new</CODE> is called to allocate the raw memory, then a constructor is called to initialize an object in that memory. In particular, <CODE>operator</CODE> <CODE>new</CODE> sets <CODE>onTheHeap</CODE> to true, and each constructor checks <CODE>onTheHeap</CODE> to see if <A NAME="p149"></A>the raw memory of the object being constructed was allocated by <CODE>operator</CODE> <CODE>new</CODE>. If not, an exception of type <CODE>HeapConstraintViolation</CODE> is thrown. Otherwise, construction proceeds as usual, and when construction is finished, <CODE>onTheHeap</CODE> is set to false, thus resetting the default value for the next object to be <NOBR>constructed.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P><A NAME="22057"></A>
<P><A NAME="dingp17"></A>This is a nice enough idea, but it won't work. Consider this potential client <NOBR>code:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="22060"></A>

⌨️ 快捷键说明

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