📄 5resource.html
字号:
{
public:
AssignNode (auto_ptr<Node> & pLeft, auto_ptr<Node> & pRight)
: BinNode (pLeft, pRight)
{}
double Calc () const;
};</pre>
</td></tr>
</table>
<!-- End Code -->
<p>As before, we were able to get rid of explicit destructors.
<p>We can continue our destructor derby with the <var>Parser</var> itself. It can own its parsing tree through an <var>auto_ptr</var> and gone is its explicit destructor.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre> auto_ptr<Node> _pTree;</pre>
</td></tr>
</table>
<!-- End Code -->
<p>We can't get rid of the destructor of <var>MultiNode</var> yet. For the time being, let's just rewrite it so that it accepts <var>auto_ptr</var>s as arguments to its methods. We'll return to the issue "owning containers" later.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>class MultiNode: public Node
{
public:
MultiNode (auto_ptr<Node> & pNode)
{
AddChild (pNode, true);
}
~MultiNode ();
void AddChild (auto_ptr<Node> & pNode, bool isPositive)
{
_aChild.push_back (pNode.release ());
_aIsPositive.push_back (isPositive);
}
protected:
std::vector<Node*> _aChild;
std::vector<bool> _aIsPositive;
};</pre>
</td></tr>
</table>
<!-- End Code -->
<p>And now let's look at the <var>Parser::Expr</var> method after it's been generously sprinkled with <var>auto_ptr</var>s.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre><font color="red">auto_ptr<Node></font> Parser::Expr ()
{
// Parse a term
<font color="red">auto_ptr<Node></font> pNode = Term ();
if (pNode.<font color="red">get ()</font> == 0)
return pNode;
EToken token = _scanner.Token ();
if (token == tPlus || token == tMinus)
{
// Expr := Term { ('+' | '-') Term }
<font color="red">auto_ptr<MultiNode></font> pMultiNode (new SumNode (pNode));
do
{
_scanner.Accept ();
<font color="red">auto_ptr<Node></font> pRight = Term ();
pMultiNode->AddChild (pRight, (token == tPlus));
token = _scanner.Token ();
} while (token == tPlus || token == tMinus);
// with member template support
pNode = pMultiNode; // <- Up-casting!
// pNode = <font color="red">up_cast<Node></font> (pMultiNode);
}
else if (token == tAssign)
{
_scanner.Accept ();
// Expr := Term = Expr
<font color="red">auto_ptr<Node></font> pRight = Expr ();
// provided the Term is an lvalue
if (pNode->IsLvalue ())
{
// Assignment node
pNode = <font color="red">auto_ptr<Node></font> (new AssignNode (pNode, pRight));
}
else
{
_status = stError;
pNode = Expr ();
}
}
// otherwise Expr := Term
return pNode;
}</pre>
</td></tr>
</table>
<!-- End Code -->
<p>There is one tricky point in this code that requires some explaining. We are dealing with two types of <var>auto_ptr</var>s--one encapsulating a <var>Node</var> and another encapsulating a <var>MultiNode</var> (only <var>MultiNode</var> has the <var>AddChild</var> method). At some point, when we are done adding children, we must convert a pointer to <var>MultiNode</var> to a pointer to <var>Node</var>. With pointers it was no trouble, because a <var>MultiNode</var> "is-a" <var>Node</var> (they are related through inheritance). But <var>auto_ptr<Node></var> is a totally different class from <var>auto_ptr<MultiNode></var>. So this simple line of code:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>pNode = pMultiNode; // <- Up-casting!</pre>
</td></tr>
</table>
<!-- End Code -->
hides some pretty interesting stuff. In fact, there is a special mechanism called <b><i>member template</i></b> that's used inside the <var>auto_ptr</var> to make possible this types of conversions (called up-casting, because they cast the pointer up the class hierarchy).
<p>Look a the following code--a transfer constructor and an assignment operator override are added to the <var>auto_ptr</var> template.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>template<class T>
class auto_ptr
{
public:
...
// "transfer" from derived class
template<class U>
auto_ptr (auto_ptr<U> & pSrc)
{
_p = pSrc.release ();
}
// assignment of derived class
template<class U>
auto_ptr & operator= (auto_ptr<U> & pSrc)
{
if (this != &pSrc)
_p = pSrc.release ();
return *this;
}
};</pre>
</td></tr>
</table>
<!-- End Code -->
These new methods are templates themselves. They are parametrized by another type, <var>U</var>.
<p>The new transfer constructor can be used to construct an <var>auto_ptr</var> of one type, parametrized by <var>T</var>, from an <var>auto_ptr</var> of another type, parametrized by <var>U</var>. So, in fact, this constructor is a template parametrized by two types, <var>T</var> and <var>U</var>.
<p>The new overload of the assignment operator can accept an <var>auto_ptr</var> parametrized by an arbitrary type <var>U</var>. It's this override that's invoked in the statement:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>pNode = pMultiNode; // <- Up-casting from MultiNode to Node</pre>
</td></tr>
</table>
<!-- End Code -->
Here, <var>pNode</var> is of the type <var>auto_ptr<Node></var> and <var>pMultiNode</var> is of the type <var>auto_ptr<MultiNode></var>. The compiler will automatically try to instantiate the appropriate implementation of the method <var>auto_ptr<Node>::operator= (auto_ptr<MultiNode> &)</var>. And here's where type safety kicks in. When the compiler generates the line from the template:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>_p = pSrc.release ();</pre>
</td></tr>
</table>
<!-- End Code -->
it has to assign a pointer to <var>MultiNode</var> returned from <var>pSrc.release ()</var>, to a pointer to <var>Node</var>, <var>_p</var>. The conversion works because of the is-a relationhip between the two classes. Had we tried the opposite conversion:
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>pMultiNode = pNode; // <- Error! Down-casting</pre>
</td></tr>
</table>
<!-- End Code -->
the compiler would generate an error, because it would fail in the conversion of <var>Node *</var> to <var>MultiNode *</var> during the instantiation of the appropriate template.
<p>The bottom line is that the assignment of <var>auto_ptr</var>s will only work for those types for which the assignments of pointers works. Notice that the types don't have to be related by inheritance. You can, for instance, implicitly convert a pointer to <var>Node</var> to a <var>void</var> pointer. It follows than that you can convert <var>auto_ptr<Node></var> to <var>auto_ptr<void></var>.
<!-- Sidebar -->
<table width="100%" border=0 cellpadding=5><tr>
<td width=10> </td>
<td bgcolor="#cccccc" class=sidebar>
If your compiler doesn't support member templates, don't despair! I have a workaround for you. Since you won't be able to do implicit <var>auto_ptr</var> conversion, you'll have to do it explicitly.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>pNode = <font color="red">up_cast<Node></font> (pMultiNode);</pre>
</td></tr>
</table>
<!-- End Code -->
I created the <var>up_cast</var> function template, parametrized by two types, <var>To</var> and <var>From</var>.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>template<class To, class From>
auto_ptr<To> <b>up_cast</b> (auto_ptr<From> & from)
{
return auto_ptr<To> (from.release ());
}</pre>
</td></tr>
</table>
<!-- End Code -->
Notice that, when I invoke this template, I don't have to specify the second type, <var>From</var>. The compiler can figure it out by looking at the type of the argument (<var>pMultiNode</var>, in this case). As before, the type checking is done by the compiler when instantiating this template for the requested pair of types.
</td></table>
<!-- End Sidebar -->
<p>It's pretty straightforward to convert the rest of the <var>Parser</var> to use <var>auto_ptr</var>s.
<p class=summary>To summarize: Memory resources can be safely kept and transferred from one point to another using <var>auto_ptr</var>s. You can easily transform and existing program to use Resource Management techniques. Just search your project for all calls to <var>new</var> and make sure the resulting pointers are safely tucked inside <var>auto_ptr</var>s.
<h3>Safe Containers</h3>
<p>We are not done yet. There are still some parts of our program that are not 100% resource safe. Let's go back to one such place inside the class <var>MultiNode</var>.
<!-- Code -->
<table width="100%" cellspacing=10><tr>
<td class=codeTable>
<pre>class MultiNode: public Node
{
public:
MultiNode (auto_ptr<Node> & pNode)
{
AddChild (pNode, true);
}
~MultiNode ();
void AddChild (auto_ptr<Node> & pNode, bool isPositive)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -