📄 mc5.htm
字号:
Printer * Printer::makePrinter()
{ return new Printer; }
<A NAME="82900"></A>
Printer * Printer::makePrinter(const Printer& rhs)
{ return new Printer(rhs); }
</PRE>
</UL>
<P><A NAME="dingp67"></A><A NAME="37826"></A>
Don't be surprised if your compilers get all upset about the declaration of <CODE>Printer</CODE>::<CODE>maxObjects</CODE> in the class definition above. In particular, be <A NAME="p141"></A>prepared for them to complain about the specification of 10 as an initial value for that variable. The ability to specify initial values for static <CODE>const</CODE> members (of integral type, e.g., <CODE>int</CODE>s, <CODE>char</CODE>s, enums, etc.) inside a class definition was added to C++ only relatively recently, so some compilers don't yet allow it. If your compilers are as-yet-unupdated, pacify them by declaring <CODE>maxObjects</CODE> to be an enumerator inside a private anonymous <NOBR>enum,<SCRIPT>create_link(67);</SCRIPT>
</NOBR></P>
<A NAME="60646"></A>
<UL><PRE>class Printer {
private:
enum { maxObjects = 10 }; // within this class,
... // maxObjects is the
}; // constant 10
</PRE>
</UL>
<P><A NAME="dingp68"></A><A NAME="60644"></A>
or by initializing the constant static like a non-<CODE>const</CODE> static <NOBR>member:<SCRIPT>create_link(68);</SCRIPT>
</NOBR></P>
<A NAME="37821"></A>
<UL><PRE>class Printer {
private:
static const size_t maxObjects; // no initial value given
<A NAME="8432"></A>
...
<A NAME="8431"></A>
};
<A NAME="37864"></A>
// this goes in a single implementation file
const size_t Printer::maxObjects = 10;
</PRE>
</UL>
<P><A NAME="dingp69"></A><A NAME="37822"></A>
This latter approach has the same effect as the original code above, but explicitly specifying the initial value is easier for other programmers to understand. When your compilers support the specification of initial values for <CODE>const</CODE> static members in class definitions, you should take advantage of that <NOBR>capability.<SCRIPT>create_link(69);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp70"></A><font ID="mhtitle">An Object-Counting Base Class</font><SCRIPT>create_link(70);</SCRIPT>
</P>
<P><A NAME="dingp71"></A><A NAME="23951"></A>
Initialization of statics aside, the approach above works like the proverbial charm, but there is one aspect of it that continues to nag. If we had a lot of classes like <CODE>Printer</CODE> whose instantiations needed to be limited, we'd have to write this same code over and over, once per class. That would be mind-numbingly dull. Given a fancy-pants language like C++, it somehow seems we should be able to automate the process. Isn't there a way to encapsulate the notion of counting instances and bundle it into a <NOBR>class?<SCRIPT>create_link(71);</SCRIPT>
</NOBR></P> <P><A NAME="dingp72"></A><A NAME="23959"></A>
We can easily come up with a base class for counting object instances and have classes like <CODE>Printer</CODE> inherit from that, but it turns out we can do even better. We can actually come up with a way to encapsulate the whole counting kit and kaboodle, by which I mean not only the functions to manipulate the instance count, but also the instance count itself. (We'll see the need for a similar trick when we examine reference counting in <A HREF="#6073">Item 29</A>. For a detailed examination of this design, see <A HREF="../MAGAZINE/CO_FRAME.HTM" TARGET="_top">my article on counting objects</A>.)<SCRIPT>create_link(72);</SCRIPT>
</P> <P><A NAME="dingp73"></A><A NAME="23999"></A>
<A NAME="p142"></A>The counter in the <CODE>Printer</CODE> class is the static variable <CODE>numObjects</CODE>, so we need to move that variable into an instance-counting class. However, we also need to make sure that each class for which we're counting instances has a <I>separate</I> counter. Use of a counting class <I>template</I> lets us automatically generate the appropriate number of counters, because we can make the counter a static member of the classes generated from the <NOBR>template:<SCRIPT>create_link(73);</SCRIPT>
</NOBR></P>
<A NAME="23991"></A>
<UL><PRE>template<class BeingCounted>
class Counted {
public:
class TooManyObjects{}; // for throwing exceptions
<A NAME="57645"></A>
static int objectCount() { return numObjects; }
<A NAME="57649"></A>
protected:
Counted();
Counted(const Counted& rhs);
<A NAME="57646"></A>
~Counted() { --numObjects; }
<A NAME="57639"></A>
private:
static int numObjects;
static const size_t maxObjects;
<A NAME="72275"></A>
void init(); // to avoid ctor code
}; // duplication
<A NAME="24006"></A>
template<class BeingCounted>
Counted<BeingCounted>::Counted()
{ init(); }
<A NAME="72288"></A>
template<class BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>&)
{ init(); }
<A NAME="72277"></A>
template<class BeingCounted>
void Counted<BeingCounted>::init()
{
if (numObjects >= maxObjects) throw TooManyObjects();
++numObjects;
}
</PRE>
</UL>
<P><A NAME="dingp74"></A><A NAME="24046"></A>
The classes generated from this template are designed to be used only as base classes, hence the protected constructors and destructor. Note the use of the private member function <CODE>init</CODE> to avoid duplicating the statements in the two <CODE>Counted</CODE> <NOBR>constructors.<SCRIPT>create_link(74);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp75"></A><A NAME="24116"></A>
We can now modify the <CODE>Printer</CODE> class to use the <CODE>Counted</CODE> <NOBR>template:<SCRIPT>create_link(75);</SCRIPT>
</NOBR></P>
<A NAME="24052"></A>
<UL><PRE><A NAME="p143"></A>class Printer: private Counted<Printer> {
public:
// pseudo-constructors
static Printer * makePrinter();
static Printer * makePrinter(const Printer& rhs);
<A NAME="24056"></A>
~Printer();
<A NAME="24057"></A>
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
...
<A NAME="57656"></A>
using Counted<Printer>::objectCount; // see below
using Counted<Printer>::TooManyObjects; // see below
<A NAME="57659"></A>
private:
Printer();
Printer(const Printer& rhs);
};
</PRE>
</UL>
<P><A NAME="dingp76"></A><A NAME="24047"></A>
The fact that <CODE>Printer</CODE> uses the <CODE>Counted</CODE> template to keep track of how many <CODE>Printer</CODE> objects exist is, frankly, nobody's business but the author of <CODE>Printer</CODE>'s. Such implementation details are best kept private, and that's why private inheritance is used here (see <A HREF="../EC/EC6_FR.HTM#21052" TARGET="_top">Item E42</A>). The alternative would be to use public inheritance between <CODE>Printer</CODE> and <CODE>Counted<Printer></CODE>, but then we'd be obliged to give the <CODE>Counted</CODE> classes a virtual destructor. (Otherwise we'd risk incorrect behavior if somebody deleted a <CODE>Printer</CODE> object through a <CODE>Counted<Printer>*</CODE> pointer — see <A HREF="../EC/EC3_FR.HTM#223029" TARGET="_top">Item E14</A>.) As <A HREF="./MC4_FR.HTM#41284" TARGET="_top">Item 24</A> makes clear, the presence of a virtual function in <CODE>Counted</CODE> would almost certainly affect the size and layout of objects of classes inheriting from <CODE>Counted</CODE>. We don't want to absorb that overhead, and the use of private inheritance lets us avoid <NOBR>it.<SCRIPT>create_link(76);</SCRIPT>
</NOBR></P> <P><A NAME="dingp77"></A><A NAME="24192"></A>
Quite properly, most of what <CODE>Counted</CODE> does is hidden from <CODE>Printer</CODE>'s clients, but those clients might reasonably want to find out how many <CODE>Printer</CODE> objects exist. The <CODE>Counted</CODE> template offers the <CODE>objectCount</CODE> function to provide this information, but that function becomes private in <CODE>Printer</CODE> due to our use of private inheritance. To restore the public accessibility of that function, we employ a <CODE>using</CODE> <NOBR>declaration:<SCRIPT>create_link(77);</SCRIPT>
</NOBR></P>
<A NAME="24209"></A>
<UL><PRE>class Printer: private Counted<Printer> {
public:
...
using Counted<Printer>::objectCount; // make this function
// public for clients
... // of Printer
};
</PRE>
</UL>
<P><A NAME="dingp78"></A><A NAME="24228"></A>
<A NAME="p144"></A>This is perfectly legitimate, but if your compilers don't yet support namespaces, they won't allow it. If they don't, you can use the older access declaration <NOBR>syntax:<SCRIPT>create_link(78);</SCRIPT>
</NOBR></P>
<A NAME="24237"></A>
<UL><PRE>class Printer: private Counted<Printer> {
public:
...
Counted<Printer>::objectCount; // make objectCount
// public in Printer
...
<A NAME="75655"></A>
};
</PRE>
</UL>
<P><A NAME="dingp79"></A><A NAME="24234"></A>
This more traditional syntax has the same meaning as the <CODE>using</CODE> declaration, but it's deprecated. The class <CODE>TooManyObjects</CODE> is handled in the same fashion as <CODE>objectCount</CODE>, because clients of <CODE>Printer</CODE> must have access to <CODE>TooManyObjects</CODE> if they are to be able to catch exceptions of that <NOBR>type.<SCRIPT>create_link(79);</SCRIPT>
</NOBR></P> <P><A NAME="dingp80"></A><A NAME="24254"></A>
When <CODE>Printer</CODE> inherits from <CODE>Counted<Printer></CODE>, it can forget about counting objects. The class can be written as if somebody else were doing the counting for it, because somebody else (<CODE>Counted<Printer></CODE>) is. A <CODE>Printer</CODE> constructor now looks like <NOBR>this:<SCRIPT>create_link(80);</SCRIPT>
</NOBR></P>
<A NAME="24257"></A>
<UL><PRE>Printer::Printer()
{
<I>proceed with normal object construction;</I>
}
</PRE>
</UL>
<P><A NAME="dingp81"></A><A NAME="24255"></A>
What's interesting here is not what you see, it's what you don't. No checking of the number of objects to see if the limit is about to be exceeded, no incrementing the number of objects in existence once the constructor is done. All that is now handled by the <CODE>Counted<Printer></CODE> constructors, and because <CODE>Counted<Printer></CODE> is a base class of <CODE>Printer</CODE>, we know that a <CODE>Counted<Printer></CODE> constructor will always be called before a <CODE>Printer</CODE> constructor. If too many objects are created, a <CODE>Counted<Printer></CODE> constructor throws an exception, and the <CODE>Printer</CODE> constructor won't even be invoked. Nifty, <NOBR>huh?<SCRIPT>create_link(81);</SCRIPT>
</NOBR></P> <P><A NAME="dingp82"></A><A NAME="24275"></A>
Nifty or not, there's one loose end that demands to be tied, and that's the mandatory definitions of the statics inside <CODE>Counted</CODE>. It's easy enough to take care of <CODE>numObjects</CODE> — we just put this in <CODE>Counted</CODE>'s implementation <NOBR>file:<SCRIPT>create_link(82);</SCRIPT>
</NOBR></P>
<A NAME="24281"></A>
<UL><PRE>
template<class BeingCounted> // defines numObjects
int Counted<BeingCounted>::numObjects; // and automatically
// initializes it to 0
</PRE>
</UL>
<P><A NAME="dingp83"></A><A NAME="24312"></A>
The situation with <CODE>maxObjects</CODE> is a bit trickier. To what value should we initialize this variable? If we want to allow up to 10 printers, we should initialize <CODE>Counted<Printer></CODE>::<CODE>maxObjects</CODE> to 10. If, on the <A NAME="p145"></A>other hand, we want to allow up to 16 file descriptor objects, we should initialize <CODE>Counted<FileDescriptor></CODE>::<CODE>maxObjects</CODE> to 16. What to <NOBR>do?<SCRIPT>create_link(83);</SCRIPT>
</NOBR></P> <P><A NAME="dingp84"></A><A NAME="24276"></A>
We take the easy way out: we do nothing. We provide no initialization at all for <CODE>maxObjects</CODE>. Instead, we require that <i>clients</i> of the class provide the appropriate initialization. The author of <CODE>Printer</CODE> must add this to an implementation <NOBR>file:<SCRIPT>create_link(84);</SCRIPT>
</NOBR></P><A NAME="24320"></A>
<UL><PRE>const size_t Counted<Printer>::maxObjects = 10;
</PRE>
</UL><P><A NAME="dingp85"></A><A NAME="24318"></A>
<A NAME="dingp85"></A>Similarly, the author of <CODE>FileDescriptor</CODE> must add <NOBR>this:<SCRIPT>create_link(85);</SCRIPT>
</NOBR></P><A NAME="24325"></A>
<UL><PRE>const size_t Counted<FileDescriptor>::maxObjects = 16;
</PRE>
</UL><P><A NAME="dingp86"></A><A NAME="24323"></A>
What will happen if these authors forget to provide a suitable definition for <CODE>maxObjects</CODE>? Simple: they'll get an error during linking, because <CODE>maxObjects</CODE> will be undefined. Provided we've adequately documented this requirement for clients of <CODE>Counted</CODE>, they can then say "Duh" to themselves and go back and add the requisite <NOBR>initialization.<SCRIPT>create_link(86);</SCRIPT>
</NOBR></P>
<!-- SectionName="M27: Requiring or prohibiting heap-based objects" -->
<A NAME="22627"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#5350">Item 26: Limiting the number of objects of a class</A> <BR> Continue to <A HREF="#61766">Item 28: Smart pointers</A></FONT></DIV>
<P><A NAME="dingp87"></A><font ID="mititle">Item 27: Requiring or prohibiting heap-based objects.</font><SCRIPT>create_link(87);</SCRIPT>
</P>
<A NAME="72177"></A><A NAME="22628"></A>
<P><A NAME="dingp88"></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>th
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -