📄 chapter14.html
字号:
<B>static</B> member functions cannot be <B>virtual</B> (a topic covered
thoroughly in Chapter 15).</FONT><A NAME="_Toc472654999"></A><BR></P></DIV>
<A NAME="Heading416"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Choosing composition vs.
inheritance<A NAME="CompileDB"></A><BR><A NAME="Index2342"></A><A NAME="Index2343"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Both composition and inheritance place
subobjects<A NAME="Index2344"></A><A NAME="Index2345"></A><A NAME="Index2346"></A>
inside your new class. Both use the constructor initializer list to construct
these subobjects. You may now be wondering what the difference is between the
two, and when to choose one over the other.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Composition is generally used when you
want the features of an existing class inside your new class, but not its
interface. That is, you embed an object to implement features of your new class,
but the user of your new class sees the interface you’ve defined rather
than the interface from the original class. To do this, you follow the typical
path of embedding <B>private</B> objects of existing classes inside your new
class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Occasionally, however, it makes sense to
allow the class user to directly access the composition of your new class, that
is, to make the member objects <B>public</B>. The member objects use access
control themselves, so this is a safe thing to do and when the user knows
you’re assembling a bunch of parts, it makes the interface easier to
understand. A <B>Car</B> class is a good example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:Car.cpp</font>
<font color=#009900>// Public composition</font>
<font color=#0000ff>class</font> Engine {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> start() <font color=#0000ff>const</font> {}
<font color=#0000ff>void</font> rev() <font color=#0000ff>const</font> {}
<font color=#0000ff>void</font> stop() <font color=#0000ff>const</font> {}
};
<font color=#0000ff>class</font> Wheel {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> inflate(<font color=#0000ff>int</font> psi) <font color=#0000ff>const</font> {}
};
<font color=#0000ff>class</font> Window {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> rollup() <font color=#0000ff>const</font> {}
<font color=#0000ff>void</font> rolldown() <font color=#0000ff>const</font> {}
};
<font color=#0000ff>class</font> Door {
<font color=#0000ff>public</font>:
Window window;
<font color=#0000ff>void</font> open() <font color=#0000ff>const</font> {}
<font color=#0000ff>void</font> close() <font color=#0000ff>const</font> {}
};
<font color=#0000ff>class</font> Car {
<font color=#0000ff>public</font>:
Engine engine;
Wheel wheel[4];
Door left, right; <font color=#009900>// 2-door</font>
};
<font color=#0000ff>int</font> main() {
Car car;
car.left.window.rollup();
car.wheel[0].inflate(72);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the composition of a <B>Car</B>
is part of the analysis of the problem (and not simply part of the underlying
design), making the members <B>public</B> assists the client programmer’s
understanding of how to use the class and requires less code complexity for the
creator of the class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">With a little thought, you’ll also
see that it would make no sense to compose a <B>Car</B> using a
“vehicle” object – a car doesn’t contain a vehicle, it
<I>is</I> a vehicle. The <I>is-a</I> relationship is expressed with inheritance,
and the <I>has-a</I> relationship is expressed with
composition.</FONT><A NAME="_Toc312374024"></A><A NAME="_Toc472655000"></A><BR></P></DIV>
<A NAME="Heading417"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Subtyping<BR><A NAME="Index2347"></A><A NAME="Index2348"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now suppose you want to create a type of
<B>ifstream</B> <A NAME="Index2349"></A>object that not only opens a file but
also keeps track of the name of the file. You can use composition and embed both
an <B>ifstream</B> and a <B>string</B> into the new class:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:FName1.cpp</font>
<font color=#009900>// An fstream with a file name</font>
#include <font color=#004488>"../require.h"</font>
#include <iostream>
#include <fstream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> FName1 {
ifstream file;
string fileName;
<font color=#0000ff>bool</font> named;
<font color=#0000ff>public</font>:
FName1() : named(<font color=#0000ff>false</font>) {}
FName1(<font color=#0000ff>const</font> string& fname)
: fileName(fname), file(fname.c_str()) {
assure(file, fileName);
named = <font color=#0000ff>true</font>;
}
string name() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> fileName; }
<font color=#0000ff>void</font> name(<font color=#0000ff>const</font> string& newName) {
<font color=#0000ff>if</font>(named) <font color=#0000ff>return</font>; <font color=#009900>// Don't overwrite</font>
fileName = newName;
named = <font color=#0000ff>true</font>;
}
<font color=#0000ff>operator</font> ifstream&() { <font color=#0000ff>return</font> file; }
};
<font color=#0000ff>int</font> main() {
FName1 file(<font color=#004488>"FName1.cpp"</font>);
cout << file.name() << endl;
<font color=#009900>// Error: close() not a member:</font>
<font color=#009900>//! file.close();</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s a problem here, however. An
attempt is made to allow the use of the <B>FName1</B> object anywhere an
<B>ifstream</B> object is used by including an automatic type conversion
operator from <B>FName1</B> to an <B>ifstream&</B>. But in main, the
line</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>file.close();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">will not compile because automatic type
conversion happens only in function calls, not during member selection. So this
approach won’t work.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A second approach is to add the
definition of <B>close( )</B> to <B>FName1</B>:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font> close() { file.close(); }</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This will work if there are only a few
functions you want to bring through from the <B>ifstream</B> class. In that case
you’re only using part of the class, and composition
<A NAME="Index2350"></A>is appropriate.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But what if you want everything in the
class to come through? This is called <I>subtyping</I> because you’re
making a new type from an existing type, and you want your new type to have
exactly the same interface as the existing type (plus any other member functions
you want to add), so you can use it everywhere you’d use the existing
type. This is where inheritance is essential. You can see that subtyping solves
the problem in the preceding example perfectly:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:FName2.cpp</font>
<font color=#009900>// Subtyping solves the problem</font>
#include <font color=#004488>"../require.h"</font>
#include <iostream>
#include <fstream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> FName2 : <font color=#0000ff>public</font> ifstream {
string fileName;
<font color=#0000ff>bool</font> named;
<font color=#0000ff>public</font>:
FName2() : named(<font color=#0000ff>false</font>) {}
FName2(<font color=#0000ff>const</font> string& fname)
: ifstream(fname.c_str()), fileName(fname) {
assure(*<font color=#0000ff>this</font>, fileName);
named = <font color=#0000ff>true</font>;
}
string name() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> fileName; }
<font color=#0000ff>void</font> name(<font color=#0000ff>const</font> string& newName) {
<font color=#0000ff>if</font>(named) <font color=#0000ff>return</font>; <font color=#009900>// Don't overwrite</font>
fileName = newName;
named = <font color=#0000ff>true</font>;
}
};
<font color=#0000ff>int</font> main() {
FName2 file(<font color=#004488>"FName2.cpp"</font>);
assure(file, <font color=#004488>"FName2.cpp"</font>);
cout << <font color=#004488>"name: "</font> << file.name() << endl;
string s;
getline(file, s); <font color=#009900>// These work too!</font>
file.seekg(-200, ios::end);
file.close();
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now any member function available for an
<B>ifstream</B> object is available for an <B>FName2</B> object. You can also
see that non-member functions like <B>getline( ) </B>that expect an
<B>ifstream </B>can also work with an <B>FName2</B>.<B> </B>That’s because
an <B>FName2</B> <I>is</I> a type of <B>ifstream</B>; it doesn’t simply
contain one. This is a very important issue that will be explored at the end of
this chapter and in the next
one.</FONT><A NAME="_Toc312374026"></A><A NAME="_Toc472655001"></A><BR></P></DIV>
<A NAME="Heading418"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
private inheritance<BR><A NAME="Index2351"></A><A NAME="Index2352"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can inherit a base class privately by
leaving off the <B>public</B> in the base-class list, or by explicitly saying
<B>private</B> (probably a better policy because it is clear to the user that
you mean it). When you inherit privately, you’re “implementing in
terms of;” that is, you’re creating a new class that has all of the
data and functionality of the base class, but that functionality is hidden, so
it’s only part of the underlying implementation. The class user has no
access to the underlying functionality, and an object cannot be treated as a
instance of the base class (as it was in <B>FName2.cpp</B>).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You may wonder what the purpose of
<B>private</B> inheritance is, because the alternative of using composition to
create a <B>private</B> object in the new class seems more appropriate.
<B>private</B> inheritance is included in the language for completeness, but if
for no other reason than to reduce confusion, you’ll usually want to use
composition rather than <B>private</B> inheritance. However, there may
occasionally be situations where you want to produce part of the same interface
as the base class <I>and</I> disallow the treatment of the object as if it were
a base-class object. <B>private</B> inheritance provides this
ability.</FONT><BR></P></DIV>
<A NAME="Heading419"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Publicizing privately inherited members</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you inherit privately, all the
<B>public</B> members of the base class become <B>private</B>. If you want any
of them to be visible, just say their names (no arguments or return values)
<U>along with the <B>using </B>keyword </U>in the <B>public</B> section of the
derived class:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:PrivateInheritance.cpp</font>
<font color=#0000ff>class</font> Pet {
<font color=#0000ff>public</font>:
<font color=#0000ff>char</font> eat() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> 'a'; }
<font color=#0000ff>int</font> speak() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> 2; }
<font color=#0000ff>float</font> sleep() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> 3.0; }
<font color=#0000ff>float</font> sleep(<font color=#0000ff>int</font>) <font color=#0000ff>const</font> { <font color=#0000ff>return</font> 4.0; }
};
<font color=#0000ff>class</font> Goldfish : Pet { <font color=#009900>// Private inheritance</font>
<font color=#0000ff>public</font>:
<font color=#0000ff>using</font> Pet::eat; <font color=#009900>// Name publicizes member</font>
<font color=#0000ff>using</font> Pet::sleep; <font
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -