📄 wwwtc4answer.htm
字号:
<HTML>
<HEAD>
<TITLE> Answer What's Wrong With This Code? Volume #4</TITLE>
<META NAME="Author" CONTENT="Harold Howe">
</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH="640">
<TR>
<TD>
<H2>
Answer What's Wrong With This Code? Volume #4
</H2>
<H4>
Base class destructors
</H4>
<P>
Let's show the output of this program one more time.
</P>
<pre>
base constructor
Foo constructor
derived constructor
Foo constructor
base destructor
</pre>
<P>
If you look at the code for the <TT>Foo</TT> class, you will notice that the <TT>Foo</TT> destructor is supposed
print the message "Foo destructor". However, the output listing does not contain the destructor message for <TT>Foo</TT>.
What's going on? Why doesn't the program print the foo destructor message? It should print two of them because there were
two <TT>Foo</TT> objects created in the code. Take one more look at the output from the program. Notice that the <TT>Derived</TT>
destructor is also supposed to print a message, but that message is not shown in the program's output either.
</P>
<P>
The program never prints the foo destructor message because the foo objects are not being deleted. At first glance, this does not
appear to be correct. One of the <TT>Foo</TT> objects is a member object of the <TT>Derived</TT> class. It should be deleted automatically
when the <TT>Derived</TT> object is destroyed. The other <TT>Foo</TT> object is a member pointer. The <TT>Derived</TT> destructor deletes
it, so that <TT>Foo</TT> object ought to be destroyed as well.
</P>
<P>
The <TT>Foo</TT> objects are not being deleted because the <TT>Derived</TT> object is not being deleted. Well, actually it is being
partially deleted. The <TT>Base</TT> part of the object is being deleted. We can see this in the output of the program. The <TT>Base</TT>
destructor prints the message "base destructor". When the
<TT>main</TT> function calls <TT>delete</TT>, the <TT>Base</TT> part of the object is destroyed, but the <TT>Derived</TT> part of the
object is not.
</P>
<P>
The code contains a critical design flaw. The destructor in the base class is not declared as virtual. As a result, when the code calls
delete on the <TT>Base *</TT>, only the destructor for the base class gets called. Because there is no vtable entry for the destructor,
operator delete has no way of knowing that you are really deleting a <TT>Derived</TT> object. As far as it is concerned, you are deleting
a plain <TT>Base</TT> object.
</P>
<P>
To fix the code, declare the base class destructor to be virtual. The following code listing illustrates how to correct the code.
</P>
<pre>
<b>class</b> Base
<b>{</b>
<b>public</b><b>:</b>
Base<b>(</b><b>)</b>
<b>{</b>
cout <b><<</b> <font color="blue">"base constructor"</font> <b><<</b> endl<b>;</b>
<b>}</b>
<b>virtual</b> <b>~</b>Base<b>(</b><b>)</b>
<b>{</b>
cout <b><<</b> <font color="blue">"base destructor"</font> <b><<</b> endl<b>;</b>
<b>}</b>
<b>}</b><b>;</b>
</pre>
<P>
There is a bit of irony to how we fixed the code. The original problem was that the <TT>Foo</TT> objects were not being
destroyed, and the destructor for the <TT>Derived</TT> class was not getting called. So who do we blame? We can't blame the
<TT>Derived</TT> class, and we certainly can't pin any blame on <TT>Foo</TT>. It's just an innocent bystander. The culprit
is embedded deep down in the base class.
</P>
<P>
There is a rule of thumb that should be followed here (in my opinion anyway). Base class destructors should always be declared
virtual. This may not be news to you, especially if you are familiar with Scott Meyer's "Effective C++" series. Some people argue
that you don't always have to make the base class destructor virtual. If you don't delete the object by using a pointer to a base class,
then the problem disappears. For example, if the main function was coded like this, then the <TT>Derived</TT> destructor would have
been called.
</p>
<pre>
<b>int</b> main<b>(</b><b>)</b>
<b>{</b>
<font color="navy">//Base *b = new Derived;</font>
Derived <b>*</b>b <b>=</b> <b>new</b> Derived<b>;</b>
<b>delete</b> b<b>;</b>
<b>return</b> <font color="blue">0</font><b>;</b>
<b>}</b>
</pre>
<P>
I think this is a poor argument. As a designer of a base class, how can you be certain how people are going to use your class?
Forcing your users to always use a pointer to a derived object severely limits their ability to use your class. You have effectively
prevented them from using polymorphism, one of the key ingredients of object oriented programming.
</P>
<P>
To drive home this argument, let's pretend that we have a base class and two classes that derive from it called <TT>Derived1</TT> and
<TT>Derived2</TT>. Both derived classes share some common functionally in the <TT>Base</TT> class. This common base may consist of
shared code, or the common base could provide only an interface that the derived classes have to support.
</p>
<pre>
<b>class</b> Base
<b>{</b>
<b>...</b>
<b>public</b><b>:</b>
<b>virtual</b> <b>void</b> DoSomething<b>(</b><b>)</b><b>;</b>
<b>~</b>Base<b>(</b><b>)</b><b>;</b>
<b>}</b><b>;</b>
<b>class</b> Derived1 <b>:</b> <b>public</b> Base
<b>{</b>
<b>...</b> <font color="navy">// private members</font>
<b>public</b><b>:</b>
<b>virtual</b> <b>void</b> DoSomething<b>(</b><b>)</b><b>;</b>
<b>}</b><b>;</b>
<b>class</b> Derived2 <b>:</b> <b>public</b> Base
<b>{</b>
<b>...</b> <font color="navy">// private members</font>
<b>public</b><b>:</b>
<b>virtual</b> <b>void</b> DoSomething<b>(</b><b>)</b><b>;</b>
<b>}</b><b>;</b>
</pre>
<P>
Now let's say that you want to store some of these objects in a container. The container should be able to store all three types
of objects. You intend to iterate the container and call the <TT>DoSomething</TT> method on each object. When you are done, you will
need to loop through the container and delete each object. Here is how you might go about it.
</P>
<pre>
vector<Base <b>*</b><b>></b> base_vector<b>;</b>
base_vector<b>.</b>push_back<b>(</b> <b>new</b> Base<b>)</b><b>;</b>
base_vector<b>.</b>push_back<b>(</b> <b>new</b> Base<b>)</b><b>;</b>
base_vector<b>.</b>push_back<b>(</b> <b>new</b> Derived1<b>)</b><b>;</b>
base_vector<b>.</b>push_back<b>(</b> <b>new</b> Derived2<b>)</b><b>;</b>
vector<Base <b>*</b><b>></b><b>:</b><b>:</b>iterator iter <b>=</b> base_vector<b>.</b>begin<b>(</b><b>)</b><b>;</b>
<b>for</b><b>(</b><b>;</b> iter <b>!=</b> base_vector<b>.</b>end<b>(</b><b>)</b><b>;</b> <b>++</b>iter<b>)</b>
<b>{</b>
iter<b>-></b>DoSomething<b>(</b><b>)</b><b>;</b>
<b>}</b>
<b>...</b> more stuff
iter <b>=</b> base_vector<b>.</b>begin<b>(</b><b>)</b><b>;</b>
<b>for</b><b>(</b><b>;</b> iter <b>!=</b> base_vector<b>.</b>end<b>(</b><b>)</b><b>;</b> <b>++</b>iter<b>)</b>
<b>{</b>
<b>delete</b> <b>*</b>iter<b>;</b>
<b>}</b>
</pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -