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

📄 wwwtc4answer.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 2 页
字号:


<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>&lt;&lt;</b> <font color="blue">&quot;base constructor&quot;</font> <b>&lt;&lt;</b> endl<b>;</b>

    <b>}</b>



    <b>virtual</b> <b>~</b>Base<b>(</b><b>)</b>

    <b>{</b>

        cout <b>&lt;&lt;</b> <font color="blue">&quot;base destructor&quot;</font> <b>&lt;&lt;</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&lt;Base <b>*</b><b>&gt;</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&lt;Base <b>*</b><b>&gt;</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>-&gt;</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 + -