📄 index.html
字号:
Note that the relationship between <tt>Radio</tt> and <tt>ElectricAppliance</tt>
is 1:1 and corroborates the decision to derive <tt>Radio</tt> from <tt>ElectricAppliance</tt>.</p>
<h3> <a name="Heading18">The Holds-a Relation</a></h3>
<p>Ownership defines the responsibility for the creation and the destruction of
an object. An object is an owner of some other resource if and only if it has
the responsibility for both constructing and destroying it. In this respect,
an object that contains another object also owns it because its constructor
is responsible for the invocation of the embedded object's constructor. Likewise,
its destructor is responsible for invoking the embedded object's destructor.
This is the well-known has-a relationship. A similar relationship is <i>holds-a</i>.
It is distinguished from has-a by one factor: ownership. A class that indirectly
contains -- by means of a reference or a pointer -- another object that is constructed
and destroyed independently is said to hold that object. Here's an example:</p>
<pre>
<tt>class Phone {/*...*/};</tt>
<tt>class Dialer {/*...*/};</tt>
<tt>class Modem</tt>
<tt>{</tt>
<tt>private: </tt>
<tt> Phone* pline;</tt>
<tt> Dialer& dialer;</tt>
<tt>public:</tt>
<tt> Modem (Phone *pp, Dialer& d) : pline(pp), dialer {}</tt>
<tt>//Phone and Dialer objects are constructed and destroyed</tt>
<tt>//independently of Modem</tt>
<tt>};</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt> Phone phone;</tt>
<tt> Dialer dialer;</tt>
<tt> Modem modem(&phone, dialer);</tt>
<tt> //...use modem</tt>
<tt>}</tt>
</pre>
<p><tt>Modem</tt> uses <tt>Phone</tt> and <tt>Dialer</tt>. However, it is not
responsible for constructing or destroying them.</p>
<p> </p>
<h3> <a name="Heading19">Empty Classes</a></h3>
<p>A class that contains no data members and no member functions is an <i>empty
class</i>. For example</p>
<pre>
<tt>class PlaceHolder {};</tt>
</pre>
<p>An empty class can serve as a placeholder for a yet-to-be defined class. Imagine
an interface class that serves as a base for other classes; instead of waiting
for its full implementation to be completed, it can be used this way in the
interim. Additionally, an empty class can also be used as a means of forcing
derivation relationship among classes that are not originally descended from
one base class. (This is a bottom-up design). Finally, it can be used as a dummy
argument to distinguish between overloaded versions of a function. In fact,
one of the standard versions of operator <tt>new</tt> (see also Chapter 11,
"Memory Management") uses this technique:</p>
<pre>
<tt>#include <new></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> try</tt>
<tt> {</tt>
<tt> int *p = new int[100]; //exception-throwing new</tt>
<tt> }</tt>
<tt> catch(bad_alloc & new_failure) {/*..*/}</tt>
<tt> int *p = new (nothrow) int [100]; // exception-free version of</tt>
<tt> if (p) </tt>
<tt> {/*..*/}</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>The <tt>nothrow</tt> argument is of type <tt>nothrow_t</tt>, which is an empty
class by itself.</p>
<h3> <a name="Heading20">Using structs as A Shorthand for Public Classes</a></h3>
<p>Traditionally, <tt>structs</tt> serve as data aggregates. However, in C++ a
<tt>struct</tt> can have constructors, a destructor, and member functions --
just like a class. The only difference between the two is the default access
type: By default, a class has <tt>private</tt> access type to its members and
derived objects, whereas a <tt>struct</tt> has <tt>public</tt> access. Consequently,
<tt>structs</tt> are sometimes used as shorthand for classes, whose members
are all <tt>public</tt>. Abstract classes are a good example of classes that
have all public members.</p>
<pre>
<tt>#include <cstdio></tt>
<tt>using namespace std;</tt>
<tt>struct File //interface class. all members are implicitly public</tt>
<tt>{</tt>
<tt> virtual int Read() = 0;</tt>
<tt> File(FILE *);</tt>
<tt> virtual ~File() = 0;</tt>
<tt>};</tt>
<tt>class TextFile: File //implicit public inheritance; File is a struct</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> string path;</tt>
<tt>public:</tt>
<tt> int Flush();</tt>
<tt> int Read();</tt>
<tt>};</tt>
<tt>class UnicodeFile : TextFile //implicit private inheritance</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> wchar_t convert(char c);</tt>
<tt>};</tt>
</pre>
<h3> <a name="Heading21">Friendship</a></h3>
<p>A class can grant access to its members on a selective basis bydeclaring external
classes and functions as friends. A friend has full access to all the grantor's
members, including private and protected ones. Friendship is sometimes unjustly
criticized for exposing implementation details. However, this is radically different
from declaring data members as public because friendship enables the class to
declare explicitly which clients can access its members; in contrast, a <tt>public</tt>
declaration provides indiscriminate access to a member. Here's an example:</p>
<pre>
<tt>bool operator ==( const Date & d1, const Date& d2);</tt>
<tt>{</tt>
<tt> return (d1.day == d2.day) &&</tt>
<tt> (d1.month == d2.month) &&</tt>
<tt> (d1.year == d2.year);</tt>
<tt>}</tt>
<tt>class Date</tt>
<tt>{</tt>
<tt> private:</tt>
<tt> int day, month, year;</tt>
<tt> public:</tt>
<tt> friend bool operator ==( const Date & d1, const Date& d2);</tt>
<tt>};</tt>
</pre>
<p>Remember that friendship is not inherited, so nonpublic members of any class
that is derived from <tt>Date</tt> are not accessible to operator <tt>==</tt>.</p>
<h3> <a name="Heading22">Nonpublic Inheritance</a></h3>
<p>When a derived class inherits from a nonpublic base, the is-a relationship
between a derived object and its nonpublic base does not exist. For example:</p>
<pre>
<tt>class Mem_Manager {/*..*/};</tt>
<tt>class List: private Mem_Manager {/*..*/};</tt>
<tt>void OS_Register( Mem_Manager& mm);</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> List li;</tt>
<tt> OS_Register( li ); //compile time error; conversion from</tt>
<tt> //List & to Mem_Manager& is inaccessible</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>Class <tt>List</tt> has a private base, <tt>Mem_Manager</tt>, which is responsible
for its necessary memory bookkeeping. However, <tt>List</tt> is not a memory
manager by itself. Therefore, private inheritance is used to block its misuse.
Private inheritance is similar to containment. As a matter of fact, the same
effect might have been achieved by making <tt>Mem_Manager</tt> a member of class
<tt>List</tt>. <tt>Protected</tt> inheritance is used in class hierarchies for
similar purposes.</p>
<h3> <a name="Heading23">Common Root Class</a></h3>
<p>In many frameworks and software projects, all classes are forced to be descendants
of one common root class, which is usually named <tt>Object</tt>. This design
policy prevails in other OO languages such as Smalltalk and Java, whose classes
are derived from class <tt>Object</tt> implicitly. However, imitating this in
C++ incurs many compromises and potential bugs. It creates artificial kinship
among classes that have absolutely nothing in common. Bjarne Stroustrup addresses
the issue: "Now what is the common relationship between a smile, the driver
of my CD-ROM reader, a recording of Richard Strauss' Don Juan, a line of text,
my medical records, and a real-time clock? Placing them all in a single hierarchy
when their only shared property is that they are programming artifacts (they
are all "objects") is of little fundamental value and can cause confusion."
(<i>The C++ Programming Language, 3rd ed.</i>, page 732).</p>
<p>If you are looking for genericity, that is, if you need an algorithm/container/function
that works for every data type, you might find that templates serve you better.
Moreover, a common root design policy also forces you to refrain from multiple
inheritance entirely because any class that is derived simultaneously from two
or more base classes faces the <i>dreadful derivation diamond</i> problem: It
embeds more than one base subobject. Finally, the common root class usually
serves as a means of implementing exception handling and RTTI, both of which
are integral parts of C++ anyway.</p>
<h3> <a name="Heading24">Forward Declarations</a></h3>
<p>Consider the following common situation in which classes refer to one another:</p>
<pre>
<tt>//file: bank.h</tt>
<tt>class Report</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> void Output(const Account& account); // compile time error;</tt>
<tt> // Account is not declared yet</tt>
<tt>};</tt>
<tt>class Account</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> void Show() {Report::Output(*this);}</tt>
<tt>};</tt>
</pre>
<p>An attempt to compile this header file causes compilation errors because the
compiler does not recognize the identifier <tt>Account</tt> as a class name
when class <tt>Report</tt> is compiled. Even if you relocate the declaration
of class <tt>Account</tt> and place it before class <tt>Report</tt>, you encounter
the same problem: <tt>Report</tt> is referred to from <tt>Account</tt>. For
that purpose, a <i>forward declaration</i> is required. A forward declaration
instructs the compiler to hold off reporting such errors until the entire source
file has been scanned. For example </p>
<pre>
<tt>//file: bank.h</tt>
<tt>class Acount; //forward declaration</tt>
<tt>class Report</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> void Output(const Account& account); //fine</tt>
<tt>};</tt>
<tt>class Account</tt>
<tt>{</tt>
<tt>private:</tt>
<tt> Report rep;</tt>
<tt>public:</tt>
<tt> void Show() {Report::Output(*this);}</tt>
<tt>};</tt>
</pre>
<p>The <tt>forward</tt> declaration in the beginning of the source file enables
class <tt>Report</tt> to refer to class <tt>Account</tt> even though its definition
has not yet been seen. Note that only references and pointers can refer to a
<tt>forward</tt>-declared class.</p>
<h3> <a name="Heading25">Local Classes</a></h3>
<p>A class can be declared inside a function or a block. In such cases, it is
not visible from anywhere else, and instances thereof can only be created within
the scope in which it is declared. This can be useful if you need to hide an
ancillary object that is not to be accessible or used anywhere else. For example</p>
<pre>
<tt>void f(const char *text)</tt>
<tt>{</tt>
<tt> class Display //local helper class; visible only in f()</tt>
<tt> {</tt>
<tt> const char *ps;</tt>
<tt> public:</tt>
<tt> Display(const char *t) : ps(t) {}</tt>
<tt> ~Display() { cout<<ps; }</tt>
<tt> };</tt>
<tt>Display ucd(text); //local object of type Display</tt>
<tt>}</tt>
</pre>
<p>A local class has no linkage.</p>
<h3> <a name="Heading26">Multiple Inheritance</a></h3>
<p>Multiple inheritance was introduced to C++ in 1989. It isn't an exaggeration
to say that it has been the most controversial feature ever added to C++. The
opponents of multiple inheritance maintain that it adds an unnecessary complexity
to the language, that every design model that uses multiple inheritance can
be modeled with single inheritance, and that it complicates compiler writing.
Of the three arguments, only the third one is true. Multiple inheritance is
optional. Designers who feel that they can make do without it are never forced
to use it. The added level of complexity that is ascribed to multiple inheritance
is not a compelling argument either because the same criticism is applicable
to other language features such as templates, operator overloading, exception
handling, and so on.</p>
<p>Multiple inheritance enables the designer to create objects that are closer
to their real-world reality. A fax modem card is essentially a modem and a fax
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -