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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<tt>class DateTime : public Date</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  int hthiss;</tt>
<tt>  int minutes;</tt>
<tt>  int seconds;</tt>
<tt>public:</tt>
<tt>//...additional member functions</tt>
<tt>};</tt>
</pre>
<p>Now assume that class <tt>Date</tt> is used mostly on display devices, so it 
  has to supply some method of converting its <tt>d</tt>,<tt>m</tt>,<tt>y</tt> 
  members into a displayable string. In order to enhance performance, a design 
  modification is made: Instead of the three integers, a single <tt>string</tt> 
  now holds the date representation. Had class <tt>DateTime</tt> relied on the 
  internal implementation of <tt>Date</tt>, it would have had to be modified as 
  well. But because it can access <tt>Date</tt>'s data members only through access 
  methods, all that is required is a small change in the <tt>Date::Day()</tt> 
  member function. Please note that accessor methods are usually inlined anyway, 
  so their use does not incur additional runtime overhead.</p>
<h3> <a name="Heading12">Declaring Virtual Base Class Destructors</a></h3>
<p>A base class needs to have its destructor declared <tt>virtual</tt>. In doing 
  so, you ensure that the correct destructor is always called, even in the following 
  case:</p>
<pre>
<tt>class Base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  char *p;</tt>
<tt>public:</tt>
<tt>  Base() { p = new char [200]; }</tt>
<tt>  ~ Base () {delete [] p; } //non virtual destructor, bad</tt>
<tt>};</tt>
<tt>class Derived : public Base</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  char *q;</tt>
<tt>public:</tt>
<tt>  Derived() { q = new char[300]; }</tt>
<tt>  ~Derived() { delete [] q; }</tt>
<tt>  //...</tt>
<tt>};</tt>
<tt>void destroy (Base &amp; b)  </tt>
<tt>{ </tt>
<tt>  delete &amp;b; </tt>
<tt>}</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt>  Base *pb = new Derived(); //200 + 300 bytes allocated</tt>
<tt>  //... meddle with pb</tt>
<tt>  destroy (*pb);  //OOPS! only the destructor of Base is called</tt>
<tt>  //were Base's destructor virtual, the correct destructor would be called</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<h3> <a name="Heading13">Virtual Member Functions</a></h3>
<p>Virtual member functions enable subclasses to extend or override the behavior 
  of a base class. Deciding which members in a class can be overridden by a derived 
  class is not a trivial issue. A class that overrides a <tt>virtual</tt> member 
  function is only committed to adhere to the prototype of the overridden member 
  function -- not to its implementation. A common mistake is to declare all member 
  functions as <tt>virtual</tt> "just in case". In this respect, C++ makes a clear-cut 
  distinction between abstract classes that provide pure interfaces as opposed 
  to base classes that provide implementation as well as an interface.</p>
<h4> Extending A Virtual Function in A Derived Class</h4>
<p>There are cases in which you want a derived class to extend a virtual function 
  defined in its base class rather than override it altogether. It can be done 
  quite easily in the following way:</p>
<pre>
<tt>class shape</tt>
<tt>{</tt>
<tt>  //...</tt>
<tt>public:</tt>
<tt>  virtual void draw();</tt>
<tt>  virtual void resize(int x, int y) { clearscr(); /*...*/ }};</tt>
<tt>class rectangle: public shape</tt>
<tt>{</tt>
<tt>  //...</tt>
<tt>public:  </tt>
<tt>  virtual void resize (int x, int y)</tt>
<tt>  {</tt>
<tt>    shape::resize(x, y);  //explicit call to the base's virtual function</tt>
<tt>    //add functionality</tt>
<tt>    int size = x*y;</tt>
<tt>    //...</tt>
<tt>  }</tt>
<tt>};</tt>
</pre>
<p>The overriding function in a derived class should invoke an overridden function 
  of its base class using its fully-qualified name.</p>
<h4> Changing Access Specification of A Virtual Function</h4>
<p>The access specification of a <tt>virtual</tt> member function that is defined 
  in a base class can be changed in a derived class. For example</p>
<pre>
<tt>class Base</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  virtual void Say() { cout&lt;&lt;"Base";}</tt>
<tt>};</tt>
<tt>class Derived : public Base</tt>
<tt>{</tt>
<tt>private: //access specifier changed; legal but not a good idea</tt>
<tt>  void Say() {cout &lt;&lt;"Derived";} // overriding Base::Say()</tt>
<tt>};</tt>
</pre>
<p>Although this is legal, it does not work as expected when pointers or references 
  are used; a pointer or reference to <tt>Base</tt> can also be assigned to any 
  object that is publicly derived from <tt>Base</tt>:</p>
<pre>
<tt>Derived d;</tt>
<tt>Base *p = &amp;d;</tt>
<tt>p-&gt;Say(); //OK, invokes Derived::Say()</tt>
</pre>
<p>Because the actual binding of a virtual member function is postponed to runtime, 
  the compiler cannot detect that a nonpublic member function will be called; 
  it assumes that <tt>p</tt> points to an object of type <tt>Base</tt>, in which 
  <tt>Say()</tt> is a public member. As a rule, do not change the access specification 
  of a virtual member function in a derived class.</p>
<h4> Virtual Member Functions Should Not Be Private</h4>
<p></p>
<p>As you saw previously, it is customary to extend virtual functions in a derived 
  class by first invoking the base class's version of that function; then extend 
  it with additional functionality. This can't be done when a virtual function 
  is declared <tt>private</tt>. </p>
<h3> <a name="Heading14">Abstract Classes and Interfaces</a></h3>
<p>An abstract class is one that has at least one <i>pure virtual member function, 
  </i>that is, a non-implemented placeholder that must be implemented by its derived 
  class. Instances of an abstract class cannot be created because it is intended 
  to serve as a design skeleton for concrete classes that are derived from it, 
  and not as an independent object. See the following example:</p>
<pre>
<tt>class File  //abstract class; serves as interface</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  int virtual open() = 0;  //pure virtual</tt>
<tt>  int virtual close() = 0; //pure virtual	</tt>
<tt>};</tt>
<tt>class diskFile: public File</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  string filename;</tt>
<tt>  //...</tt>
<tt>public:</tt>
<tt>  int open() {/*...*/}</tt>
<tt>  int close () {/*...*/}</tt>
<tt>};</tt>
</pre>
<h3> <a name="Heading15">Use Derivation Instead of Type-Fields</a></h3>
<p>Suppose that you have to implement an internationalization helper class that 
  manages the necessary parameters of every natural language that is currently 
  supported by a word processor. A naive implementation might rely on type-fields 
  to indicate the specific language that is currently being used (for example, 
  the interface language in which menus are displayed).</p>
<pre>
<tt>class Fonts {/*...*/};</tt>
<tt>class Internationalization</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  Lang lg; //type field</tt>
<tt>  FontResthisce fonts</tt>
<tt>public:</tt>
<tt>  enum Lang {English, Hebrew, Danish}</tt>
<tt>  Internationalization(Lang lang) : lg(lang) {};</tt>
<tt>  Loadfonts(Lang lang);</tt>
<tt>};</tt>
</pre>
<p>Every modification in <tt>Internationalization</tt> affects all its users, 
  even when they are not supposed to be affected. When adding support for a new 
  language, the users of the already-supported languages have to recompile (or 
  download, which is worse) the new version of the class. Moreover, as time goes 
  by and support for new languages is added, the class becomes bigger and more 
  difficult to maintain, and it tends to contain more bugs. A much better design 
  approach is to use derivation instead of type-fields. For example</p>
<pre>
<tt>class Internationalization //now a base class</tt>
<tt>{</tt>
<tt>private:</tt>
<tt>  FontResthisce fonts</tt>
<tt>public:</tt>
<tt>  Internationalization ();</tt>
<tt>  virtual int Loadfonts();</tt>
<tt>  virtual void SetDirectionality();</tt>
<tt>};</tt>
<tt>class English : public Internationalization</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  English();</tt>
<tt>  Loadfonts() { fonts = TimesNewRoman; }</tt>
<tt>  SetDirectionality(){}//do nothing; default: left to right</tt>
<tt>};</tt>
<tt>class Hebrew : public Internationalization</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  Hebrew();</tt>
<tt>  Loadfonts() { fonts = David; }</tt>
<tt>  SetDirectionality() { directionality = right_to_left;}</tt>
<tt>};</tt>
</pre>
<p>Derivation simplifies class structure and localizes the changes that are associated 
  with a specific language to its corresponding class without affecting others. 
</p>
<h3> <a name="Heading16">Overloading A Member Function Across Class Boundaries</a></h3>
<p>A class is a namespace. The scope for overloading a member function is confined 
  to a class but not to its derived classes. Sometimes the need arises to overload 
  the same function in its class as well as in a class that is derived from it. 
  However, using an identical name in a derived class merely hides the base class's 
  function, rather than overloading it. Consider the following:</p>
<pre>
<tt>class B</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  void func();</tt>
<tt>};</tt>
<tt>class D : public B</tt>
<tt>{</tt>
<tt>public:</tt>
<tt>  void func(int n); //now hiding B::f, not overloading it</tt>
<tt>};</tt>
<tt>D d;</tt>
<tt>d.func();//compilation error. B::f is invisible in d;</tt>
<tt>d.func(1); //OK, D::func takes an argument of type int</tt>
</pre>
<p>In order to overload -- rather than hide -- a function of a base class, the 
  function name of the base class has to be injected explicitly into the namespace 
  of the derived class by a <i>using declaration</i>. For example</p>
<pre>
<tt>class D : public B</tt>
<tt>{</tt>
<tt>using B::func; // inject the name of a base member into the scope of D</tt>
<tt>public:</tt>
<tt>  void func(int n); // D now has two overloaded versions of func()</tt>
<tt>};</tt>
<tt>D d;</tt>
<tt>d.func ( ); // OK</tt>
<tt>d.func ( 10 ); // OK</tt>
</pre>
<h3> <a name="Heading17">Deciding Between Inheritance and Containment</a></h3>
<p>When designing a class hierarchy, you often face a decision between inheritance, 
  or <i>is-a</i>, and containment, or <i>has-a</i>, relation. The choice is not 
  always immediately apparent. Assume that you are designing a <tt>Radio</tt> 
  class, and you already have the following classes implemented for you in some 
  library: <tt>Dial</tt> and <tt>ElectricAppliance</tt>. It is obvious that <tt>Radio</tt> 
  is derived from <tt>ElectricAppliance</tt>. However, it is not so obvious that 
  <tt>Radio</tt> is also derived from <tt>Dial</tt>. In such cases, check whether 
  there is always a 1:1 relationship between the two. Do all radios have one and 
  only one dial? They don't. A radio can have no dials at all -- a transmitter/receiver 
  adjusted to a fixed frequency, for example. Furthermore, it might have more 
  than one dial -- FM and AM dials. Hence, your <tt>Radio</tt> class needs to 
  be designed to have Dial(s) rather than being<i> </i>derived from <tt>Dial</tt>. 

⌨️ 快捷键说明

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