📄 index.html
字号:
<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 & b) </tt>
<tt>{ </tt>
<tt> delete &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<<"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 <<"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 = &d;</tt>
<tt>p->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 + -