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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
  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&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

⌨️ 快捷键说明

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