📄 mc5.htm
字号:
<P><A NAME="dingp35"></A><A NAME="20914"></A>
It's possible, of course, that <CODE>thePrinter</CODE> strikes you as a needless addition to the global namespace. "Yes," you may say, "as a global function it looks more like a global variable, but global variables are gauche, and I'd prefer to localize all printer-related functionality inside the <CODE>Printer</CODE> class." Well, far be it from me to argue with someone who uses words like gauche. <CODE>thePrinter</CODE> can just as easily be made a static member function of <CODE>Printer</CODE>, and that puts it right where you want it. It also eliminates the need for a <CODE>friend</CODE> declaration, which many regard as tacky in its own right. Using a static member function, <CODE>Printer</CODE> looks like <NOBR>this:<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P>
<A NAME="20915"></A>
<UL><PRE><A NAME="p132"></A>class Printer {
public:
static Printer& thePrinter();
...
<A NAME="57600"></A>
private:
Printer();
Printer(const Printer& rhs);
...
<A NAME="75581"></A>
};
<A NAME="20919"></A>
Printer& Printer::thePrinter()
{
static Printer p;
return p;
}
</PRE>
</UL>
<P><A NAME="dingp36"></A><A NAME="20920"></A>
Clients must now be a bit wordier when they refer to the <NOBR>printer:<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P>
<A NAME="23806"></A>
<UL><PRE>Printer::thePrinter().reset();
Printer::thePrinter().submitJob(buffer);
</PRE>
</UL>
<P><A NAME="dingp37"></A><A NAME="23814"></A>
Another approach is to move <CODE>Printer</CODE> and <CODE>thePrinter</CODE> out of the global scope and into a <I>namespace</I> (see <A HREF="../EC/EC4_FR.HTM#6429" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 28'; return true" onMouseOut = "self.status = self.defaultStatus">Item E28</A>). Namespaces are a recent addition to C++. Anything that can be declared at global scope can also be declared in a namespace. This includes classes, structs, functions, variables, objects, typedefs, etc. The fact that something is in a namespace doesn't affect its behavior, but it does prevent name conflicts between entities in different namespaces. By putting the <CODE>Printer</CODE> class and the <CODE>thePrinter</CODE> function into a namespace, we don't have to worry about whether anybody else happened to choose the names <CODE>Printer</CODE> or <CODE>thePrinter</CODE> for themselves; our namespace prevents name <NOBR>conflicts.<SCRIPT>create_link(37);</SCRIPT>
</NOBR></P> <P><A NAME="dingp38"></A><A NAME="60498"></A>
Syntactically, namespaces look much like classes, but there are no public, protected, or private sections; everything is public. This is how we'd put <CODE>Printer</CODE> and <CODE>thePrinter</CODE> into a namespace called <CODE>PrintingStuff</CODE>:<SCRIPT>create_link(38);</SCRIPT>
</P>
<A NAME="37735"></A>
<UL><PRE>namespace PrintingStuff {
class Printer { // this class is in the
public: // PrintingStuff namespace
<A NAME="75591"></A>
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
...
<A NAME="75587"></A>
friend Printer& thePrinter();
<A NAME="71884"></A>
<A NAME="p133"></A> private:
Printer();
Printer(const Printer& rhs);
...
<A NAME="75588"></A>
};
<A NAME="37742"></A>
Printer& thePrinter() // so is this function
{
static Printer p;
return p;
}
} // this is the end of the
// namespace
</PRE>
</UL>
<P><A NAME="dingp39"></A><A NAME="37736"></A>
Given this namespace, clients can refer to <CODE>thePrinter</CODE> using a fully-qualified name (i.e., one that includes the name of the <NOBR>namespace),<SCRIPT>create_link(39);</SCRIPT>
</NOBR></P>
<A NAME="37768"></A>
<UL><PRE>PrintingStuff::thePrinter().reset();
PrintingStuff::thePrinter().submitJob(buffer);
</PRE>
</UL>
<P><A NAME="dingp40"></A><A NAME="37766"></A>
but they can also employ a <CODE><I>using</I></CODE> <I>declaration</I> to save themselves <NOBR>keystrokes:<SCRIPT>create_link(40);</SCRIPT>
</NOBR></P>
<A NAME="37776"></A>
<UL><PRE>using PrintingStuff::thePrinter; // import the name
// "thePrinter" from the
// namespace "PrintingStuff"
// into the current scope
</PRE>
</UL><A NAME="37773"></A>
<UL><PRE>thePrinter().reset(); // now thePrinter can be
thePrinter().submitJob(buffer); // used as if it were a
// local name
</PRE>
</UL>
<P><A NAME="dingp41"></A><A NAME="20559"></A>
There are two subtleties in the implementation of <CODE>thePrinter</CODE> that are worth exploring. First, it's important that the single <CODE>Printer</CODE> object be static in a <I>function</I> and not in a class. An object that's static in a class is, for all intents and purposes, <I>always</I> constructed (and destructed), even if it's never used. In contrast, an object that's static in a function is created the first time through the function, so if the function is never called, the object is never created. (You do, however, pay for a check each time the function is called to see whether the object needs to be created.) One of the philosophical pillars on which C++ was built is the idea that you shouldn't pay for things you don't use, and defining an object like our printer as a static object in a function is one way of adhering to this philosophy. It's a philosophy you should adhere to whenever you <NOBR>can.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P> <P><A NAME="dingp42"></A><A NAME="20617"></A>
There is another drawback to making the printer a class static versus a function static, and that has to do with its time of initialization. We know exactly when a function static is initialized: the first time through the function at the point where the static is defined. The situ<A NAME="p134"></A>ation with a class static (or, for that matter, a global static, should you be so gauche as to use one) is less well defined. C++ offers certain guarantees regarding the order of initialization of statics within a particular translation unit (i.e., a body of source code that yields a single object file), but it says <i>nothing</i> about the initialization order of static objects in different translation units (see <A HREF="../EC/EC7_FR.HTM#8299" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 47'; return true" onMouseOut = "self.status = self.defaultStatus">Item E47</A>). In practice, this turns out to be a source of countless headaches. Function statics, when they can be made to suffice, allow us to avoid these headaches. In our example here, they can, so why <NOBR>suffer?<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P> <P><A NAME="dingp43"></A><A NAME="40513"></A>
The second subtlety has to do with the interaction of inlining and static objects inside functions. Look again at the code for the non-member version of <CODE>thePrinter</CODE>:<SCRIPT>create_link(43);</SCRIPT>
</P>
<A NAME="40514"></A>
<UL><PRE>Printer& thePrinter()
{
static Printer p;
return p;
}
</PRE>
</UL>
<P><A NAME="dingp44"></A><A NAME="40515"></A>
Except for the first time through this function (when <CODE>p</CODE> must be constructed), this is a one-line function — it consists entirely of the statement "<CODE>return</CODE> <CODE>p</CODE>;". If ever there were a good candidate for inlining, this function would certainly seem to be the one. Yet it's not declared <CODE>inline</CODE>. Why <NOBR>not?<SCRIPT>create_link(44);</SCRIPT>
</NOBR></P> <P><A NAME="dingp45"></A><A NAME="40516"></A>
Consider for a moment why you'd declare an object to be static. It's usually because you want only a single copy of that object, right? Now consider what <CODE>inline</CODE> means. Conceptually, it means compilers should replace each call to the function with a copy of the function body, but for non-member functions, it also means something else. It means the functions in question have <I>internal linkage</I>.<SCRIPT>create_link(45);</SCRIPT>
</P> <P><A NAME="dingp46"></A><A NAME="60581"></A>
You don't ordinarily need to worry about such linguistic mumbo jumbo, but there is one thing you must remember: functions with internal linkage may be duplicated within a program (i.e., the object code for the program may contain more than one copy of each function with internal linkage), and <I>this duplication includes static objects contained within the functions</I>. The result? If you create an inline non-member function containing a local static object, you may end up with <i>more than one copy</i> of the static object in your program! So don't create inline non-member functions that contain local static data.<A HREF="#9110" onMouseOver = "self.status = 'Link to Footnote 9'; return true" onMouseOut = "self.status = self.defaultStatus"><sup>9</sup></A><SCRIPT>create_link(46);</SCRIPT>
</P> <P><A NAME="dingp47"></A><A NAME="20908"></A>
But maybe you think this business of creating a function to return a reference to a hidden object is the wrong way to go about limiting the number of objects in the first place. Perhaps you think it's better to simply count the number of objects in existence and throw an excep<A NAME="p135"></A>tion in a constructor if too many objects are requested. In other words, maybe you think we should handle printer creation like <NOBR>this:<SCRIPT>create_link(47);</SCRIPT>
</NOBR></P>
<A NAME="20924"></A>
<UL><PRE>class Printer {
public:
class TooManyObjects{}; // exception class for use
// when too many objects
// are requested
Printer();
~Printer();
<A NAME="21295"></A>
...
<A NAME="57609"></A>
private:
static size_t numObjects;
<A NAME="77862"></A>
Printer(const Printer& rhs); // there is a limit of 1
// printer, so never allow
}; // copying (see <A HREF="../EC/EC4_FR.HTM#6406" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 27'; return true" onMouseOut = "self.status = self.defaultStatus">Item E27</A>)
</PRE>
</UL>
<P><A NAME="dingp48"></A><A NAME="20970"></A>
The idea is to use <CODE>numObjects</CODE> to keep track of how many <CODE>Printer</CODE> objects are in existence. This value will be incremented in the class constructor and decremented in its destructor. If an attempt is made to construct too many Printer objects, we throw an exception of type <CODE>TooManyObjects</CODE>:<SCRIPT>create_link(48);</SCRIPT>
</P>
<A NAME="20971"></A>
<UL><PRE>// Obligatory definition of the class static
size_t Printer::numObjects = 0;
<A NAME="21001"></A>
Printer::Printer()
{
if (numObjects >= 1) {
throw TooManyObjects();
}
<A NAME="21005"></A>
<I>proceed with normal construction here;</I>
<A NAME="21009"></A>
++numObjects;
}
<A NAME="21020"></A>
Printer::~Printer()
{
<I>perform normal destruction here;</I>
<A NAME="21418"></A>
--numObjects;
}
</PRE>
</UL>
<P><A NAME="dingp49"></A><A NAME="21021"></A>
This approach to limiting object creation is attractive for a couple of reasons. For one thing, it's straightforward — everybody should be able to understand what's going on. For another, it's easy to generalize so that the maximum number of objects is some number other than <NOBR>one.<SCRIPT>create_link(49);</SCRIPT>
</NOBR></P>
<A NAME="p136"></A>
<P><A NAME="dingp50"></A><font ID="mhtitle">Contexts for Object Construction</font><SCRIPT>create_link(50);</SCRIPT>
</P>
<P><A NAME="dingp51"></A><A NAME="21060"></A>
There is also a problem with this strategy. Suppose we have a special kind of printer, say, a color printer. The class for such printers would have much in common with our generic printer class, so of course we'd inherit from <NOBR>it:<SCRIPT>create_link(51);</SCRIPT>
</NOBR></P>
<A NAME="21023"></A>
<UL><PRE>class ColorPrinter: public Printer {
...
<A NAME="75610"></A>
};
</PRE>
</UL>
<P><A NAME="dingp52"></A><A NAME="21024"></A>
Now suppose we have one generic printer and one color printer in our <NOBR>system:<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>
<A NAME="21025"></A>
<UL><PRE>Printer p;
ColorPrinter cp;
</PRE>
</UL>
<P><A NAME="dingp53"></A><A NAME="21035"></A>
How many <CODE>Printer</CODE> objects result from these object definitions? The answer is two: one for <CODE>p</CODE> and one for the <CODE>Printer</CODE> part of <CODE>cp</CODE>. At runtime, a <CODE>TooManyObjects</CODE> exception will be thrown during the construction of the base class part of <CODE>cp</CODE>. For many programmers, this is neither what they want nor what they expect. (Designs that avoid having concrete classes inherit from other concrete classes do not suffer from this problem. For details on this design philosophy, see <A HREF="./MC6_FR.HTM#10947" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 33'; return true" onMouseOut = "self.status = self.defaultStatus">Item 33</A>.)<SCRIPT>create_link(53);</SCRIPT>
</P> <P><A NAME="dingp54"></A><A NAME="21071"></A>
A similar problem occurs when <CODE>Printer</CODE> objects are contained inside other <NOBR>objects:<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<A NAME="21073"></A>
<UL><PRE>
class CPFMachine { // for machines that can
private: // copy, print, and fax
<A NAME="21473"></A>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -