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

📄 ch05.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<tt>class Modem</tt><tt>{</tt><tt>private:	</tt><tt>  Phone* pline;</tt><tt>  Dialer&amp; dialer;</tt><tt>public:</tt><tt>  Modem (Phone *pp, Dialer&amp; 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(&amp;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 &lt;new&gt;</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 &amp; 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 &lt;cstdio&gt;</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 &amp; d1, const Date&amp; d2);</tt><tt>{</tt><tt>  return (d1.day == d2.day) &amp;&amp;</tt><tt>           (d1.month == d2.month) &amp;&amp;</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 &amp; d1, const Date&amp; 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&amp; mm);</tt><tt>int main()</tt><tt>{</tt><tt>  List li;</tt><tt>  OS_Register( li ); //compile time error; conversion from</tt><tt>                     //List &amp; to Mem_Manager&amp; 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&amp; 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&amp; 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&lt;&lt;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   combined in one. Similarly, a <tt>fax_modem</tt> class that is publicly derived   from both <tt>fax</tt> and <tt>modem</tt> represents the concept of a fax/modem   better than a single inheritance model does. But the most compelling argument   in favor of multiple inheritance is that some designs cannot be realized without   it. For example, implementing the <tt>Observer</tt> pattern in Java is nearly   impossible because Java lacks multiple inheritance ("Java vs. C++ -- A Critical   Comparison," <i>C++ Report</i>, January 1997). <tt>Observer</tt> is not the   only pattern that relies on multiple inheritance -- <tt>Adapter</tt> and <tt>Bridge</tt>   also do (ibid.).</p><h4> Using Multiple Inheritance to Conjoin Features</h4><p>Derived classes can combine the functionality of several base classes simultaneously,   by means of multiple inheritance. Trying to achieve the same effect using single   inheritance can be very difficult, to say the least. For example</p><pre><tt>class Persistent //abstract base class used by</tt><tt>{</tt><tt>                        //all persistence-supporting objects</tt><tt>public:</tt><tt>  virtual void WriteObject(void *pobj, size_t sz) = 0;</tt><tt>  virtual void* ReadO

⌨️ 快捷键说明

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