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

📄 chapter14.html

📁 《C++编程思想》中文版。。。。。。。。。。。。。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
the <A NAME="Index2282"></A><A NAME="Index2283"></A><B>sizeof(Y)</B> is twice as
big as <B>sizeof(X)</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You&#8217;ll notice that the base class
is preceded by <B>public<A NAME="Index2284"></A><A NAME="Index2285"></A></B>.
During inheritance, everything defaults to <B>private</B>. If the base class
were not preceded by <B>public</B>, it would mean that all of the <B>public</B>
members of the base class would be <B>private</B> in the derived class. This is
almost never what you
want</FONT><A NAME="fnB51" HREF="#fn51">[51]</A><A NAME="Index2286"></A><FONT FACE="Georgia">;
the desired result is to keep all the <B>public</B> members of the base class
<B>public</B> in the derived class. You do this by using the <B>public</B>
keyword during inheritance.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>change(&#160;)</B>, the base-class
<B>permute(&#160;)</B> function is called. The derived class has direct access
to all the <B>public </B>base-class functions.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>set(&#160;)</B> function in the
derived class <I>redefines</I>
<A NAME="Index2287"></A><A NAME="Index2288"></A>the <B>set(&#160;)</B> function
in the base class. That is, if you call the functions <B>read(&#160;)</B> and
<B>permute(&#160;)</B> for an object of type <B>Y</B>, you&#8217;ll get the
base-class versions of those functions (you can see this happen inside
<B>main(&#160;)</B>). But if you call <B>set(&#160;)</B> for a <B>Y</B> object,
you get the redefined version. This means that if you don&#8217;t like the
version of a function you get during inheritance, you can change what it does.
(You can also add completely new functions like
<B>change(&#160;)</B>.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, when you&#8217;re redefining a
function, you may still want to call the base-class version. If, inside
<B>set(&#160;)</B>, you simply call <B>set(&#160;)</B> you&#8217;ll get the
local version of the function &#8211; a recursive function call. To call the
base-class version, you must explicitly name the base class using the scope
resolution
operator<A NAME="Index2289"></A><A NAME="Index2290"></A><A NAME="Index2291"></A>.</FONT><A NAME="_Toc305593253"></A><A NAME="_Toc305628725"></A><A NAME="_Toc312374016"></A><A NAME="_Toc472654991"></A><BR></P></DIV>
<A NAME="Heading407"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The constructor initializer list</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You&#8217;ve seen how important it is in
C++ to guarantee proper initialization, and it&#8217;s no different during
composition and inheritance. When an object is created, the compiler guarantees
that constructors for all of its subobjects are called. In the examples so far,
all of the subobjects have default constructors, and that&#8217;s what the
compiler automatically calls. But what happens if your subobjects
<A NAME="Index2292"></A>don&#8217;t have default constructors, or if you want to
change a default argument in a constructor? This is a problem because the new
class constructor doesn&#8217;t have permission to access the <B>private</B>
data elements of the subobject, so it can&#8217;t initialize them
directly.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution is simple: Call the
constructor for the subobject. C++ provides a special syntax for this, the
<I>constructor initializer
list<A NAME="Index2293"></A><A NAME="Index2294"></A><A NAME="Index2295"></A></I>.
The form of the constructor initializer list echoes the act of inheritance. With
inheritance, you put the base classes after a colon and before the opening brace
of the class body. In the constructor initializer list, you put the calls to
subobject constructors after the constructor argument list and a colon, but
before the opening brace of the function body. For a class <B>MyType</B>,
inherited from <B>Bar</B>, this might look like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType::MyType(<font color=#0000ff>int</font> i) : Bar(i) { <font color=#009900>// ...</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">if <B>Bar</B> has a constructor that
takes a single <B>int</B>
argument.</FONT><A NAME="_Toc312374017"></A><A NAME="_Toc472654992"></A><BR></P></DIV>
<A NAME="Heading408"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Member object
initialization<BR><A NAME="Index2296"></A><A NAME="Index2297"></A><A NAME="Index2298"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It turns out that you use this very same
syntax for member object initialization when using composition. For composition,
you give the names of the objects instead of the class names. If you have more
than one constructor call in the initializer list, you separate the calls with
commas:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MyType2::MyType2(<font color=#0000ff>int</font> i) : Bar(i), m(i+1) { <font color=#009900>// ...</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is the beginning of a constructor
for class <B>MyType2</B>, which is inherited from <B>Bar </B>and contains a
member object called <B>m</B>. Note that while you can see the type of the base
class in the constructor initializer list, you only see the member object
identifier.</FONT><A NAME="_Toc312374018"></A><A NAME="_Toc472654993"></A><BR></P></DIV>
<A NAME="Heading409"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Built-in types in the initializer
list<BR><A NAME="Index2299"></A><A NAME="Index2300"></A><A NAME="Index2301"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The constructor initializer list allows
you to explicitly call the constructors for member objects. In fact,
there&#8217;s no other way to call those constructors. The idea is that the
constructors are all called before you get into the body of the new
class&#8217;s constructor. That way, any calls you make to member functions of
subobjects will always go to initialized objects. There&#8217;s no way to get to
the opening brace of the constructor without <I>some</I> constructor being
called for all the member objects and base-class objects, even if the compiler
must make a hidden call to a default constructor. This is a further enforcement
of the C++ guarantee that no object (or part of an object) can get out of the
starting gate without its constructor being called.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This idea that all of the member objects
are initialized by the time the opening brace of the constructor is reached is a
convenient programming aid as well. Once you hit the opening brace, you can
assume all subobjects are properly initialized and focus on specific tasks you
want to accomplish in the constructor. However, there&#8217;s a hitch: What
about member objects of built-in types, which don&#8217;t <I>have</I>
constructors?</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To make the syntax consistent, you are
allowed to treat built-in types as if they have a single constructor, which
takes a single argument: a variable of the same type as the variable
you&#8217;re initializing. Thus, you can say</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:PseudoConstructor.cpp</font>
<font color=#0000ff>class</font> X {
  <font color=#0000ff>int</font> i;
  <font color=#0000ff>float</font> f;
  <font color=#0000ff>char</font> c;
  <font color=#0000ff>char</font>* s;
<font color=#0000ff>public</font>:
  X() : i(7), f(1.4), c('x'), s(<font color=#004488>"howdy"</font>) {}
};

<font color=#0000ff>int</font> main() {
  X x;
  <font color=#0000ff>int</font> i(100);  <font color=#009900>// Applied to ordinary definition</font>
  <font color=#0000ff>int</font>* ip = <font color=#0000ff>new</font> <font color=#0000ff>int</font>(47);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The action of these
&#8220;pseudo-constructor calls&#8221; is to perform a simple assignment.
It&#8217;s a convenient technique and a good coding style, so you&#8217;ll see
it used often.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s even possible to use the
pseudo-constructor syntax when creating a variable of a built-in type outside of
a class:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>int</font> i(100);
<font color=#0000ff>int</font>* ip = <font color=#0000ff>new</font> <font color=#0000ff>int</font>(47);</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This makes built-in types act a little
bit more like objects. Remember, though, that these are not real constructors.
In particular, if you don&#8217;t explicitly make a pseudo-constructor call, no
initialization is
performed.</FONT><A NAME="_Toc305593254"></A><A NAME="_Toc305628726"></A><A NAME="_Toc312374019"></A><A NAME="_Toc472654994"></A><BR></P></DIV>
<A NAME="Heading410"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Combining composition &amp;
inheritance<BR><A NAME="Index2302"></A><A NAME="Index2303"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, you can use composition &amp;
inheritance together. The following example shows the creation of a more complex
class using both of them.</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:Combined.cpp</font>
<font color=#009900>// Inheritance &amp; composition</font>

<font color=#0000ff>class</font> A {
  <font color=#0000ff>int</font> i;
<font color=#0000ff>public</font>:
  A(<font color=#0000ff>int</font> ii) : i(ii) {}
  ~A() {}
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> {}
};

<font color=#0000ff>class</font> B {
  <font color=#0000ff>int</font> i;
<font color=#0000ff>public</font>:
  B(<font color=#0000ff>int</font> ii) : i(ii) {}
  ~B() {}
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> {}
};

<font color=#0000ff>class</font> C : <font color=#0000ff>public</font> B {
  A a;
<font color=#0000ff>public</font>:
  C(<font color=#0000ff>int</font> ii) : B(ii), a(ii) {}
  ~C() {} <font color=#009900>// Calls ~A() and ~B()</font>
  <font color=#0000ff>void</font> f() <font color=#0000ff>const</font> {  <font color=#009900>// Redefinition</font>
    a.f();
    B::f();
  }
};

<font color=#0000ff>int</font> main() {
  C c(47);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>C</B> inherits from <B>B</B> and has a
member object (&#8220;is composed of&#8221;) of type <B>A</B>. You can see the
constructor initializer list contains calls to both the base-class constructor
and the member-object constructor.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The function <B>C::f(&#160;)</B>
redefines <B>B::f(&#160;)</B>, which it inherits, and also calls the base-class
version. In addition, it calls <B>a.f(&#160;)</B>. Notice that the only time you
can talk about redefinition of functions is during inheritance; with a member
object you can only manipulate the public interface of the object, not redefine
it. In addition, calling <B>f(&#160;)</B> for an object of class <B>C</B> would
not call <B>a.f(&#160;)</B> if <B>C::f(&#160;)</B> had not been defined, whereas
it <I>would</I> call <B>B::f(&#160;)</B>.</FONT><BR></P></DIV>
<A NAME="Heading411"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Automatic destructor
calls<BR><A NAME="Index2304"></A><A NAME="Index2305"></A></H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although you are often required to make
explicit constructor calls in the initializer list, you never need to make
explicit destructor calls because there&#8217;s only one destructor for any
class, and it doesn&#8217;t take any arguments. However, the compiler still
ensures that all destructors are called, and that means all of the destructors
in the entire hierarchy, starting with the most-derived destructor and working
back to the root.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s worth emphasizing that
constructors and destructors are quite unusual in that every one in the
hierarchy is called, whereas with a normal member function only that function is
called, but not any of the base-class versions. If you also want to call the
base-class version of a normal member function that you&#8217;re overriding, you
must do it
explicitly.</FONT><A NAME="_Toc312374020"></A><A NAME="_Toc472654995"></A><BR></P></DIV>
<A NAME="Heading412"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Order of constructor &amp; destructor calls</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It&#8217;s interesting to know the order
of constructor and destructor calls
<A NAME="Index2306"></A><A NAME="Index2307"></A><A NAME="Index2308"></A>when an
object has many subobjects. The following example shows exactly how it
works:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C14:Order.cpp</font>
<font color=#009900>// Constructor/destructor order</font>
#include &lt;fstream&gt;
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
ofstream out(<font color=#004488>"order.out"</font>);

#define CLASS(ID) <font color=#0000ff>class</font> ID { \
<font color=#0000ff>public</font>: \
  ID(<font color=#0000ff>int</font>) { out &lt;&lt; #ID <font color=#004488>" constructor\n"</font>; } \
  ~ID() { out &lt;&lt; #ID <font color=#004488>" destructor\n"</font>; } \
};

CLASS(Base1);
CLASS(Member1);
CLASS(Member2);
CLASS(Member3);
CLASS(Member4);

<font color=#0000ff>class</font> Derived1 : <font color=#0000ff>public</font> Base1 {
  Member1 m1;
  Member2 m2;

⌨️ 快捷键说明

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