Template security (continued)
In the second part of unusual security, I told questions that lead to resource leaks in the constructor and the destructive function. This will explore two other problems. And at the end of the recommended reading list.
1.1 Problem # 2: Get
Last, I define x :: get () as:
T get ()
{
Return * Value_;
}
This definition is a little short. Since Get () does not change the Wrapper object, I should declare it as a Const member:
T get () const
{
Return * Value_;
}
GET () returns a temporary object. This temporary object is implicitly generated according to * Value_ via the copy constructor of T, and this constructor may be throwing. To avoid this, we should modify the get () to do not return anything:
Void Get (T & Value) Const throw ()
{
Value = * value_;
}
Now, get () accepts a reference to the T object in advance and the result is referenced by reference. Because get () is now not called T's constructor, it is unusually safe.
really?
Unfortunately, the answer is "NO". We just change a problem with another problem, because statement
Value = * value_;
in reality
Value.operator = (* Value_);
And it may throw it. More complete solution is
Void Get (T & Value) Const throw ()
{
Try
{
Value = * value_;
}
Catch (...)
{
}
}
Now, get () will not leak an exception.
However, work has not been completed yet. If Operator = is impurved when it assigns values to Value, Value will be in an uncertain state. Get () wants to have the maximum robust interface, it must have one of them:
l value is fully set according to * Value_, or
l Value is not changed.
These two have to get us up: no matter what method we use to solve this problem, we must call Operator = to set the value, and if Operator = throws an exception, Value will only be partially changed.
Our strong interface looks beautiful but not. We can't make it simply, you can only provide a weak commitment:
l value is fully set according to * Value_, or
l Value is in an uncertain (error) state.
But there is still a problem without solving: Let the caller know if the value of the return is "good". A possible solution (also ironic) is throwing an exception. Another possible method, is also the way I use here is to return an error code.
The modified GET () is:
Bool Get (T & Value) Const Throw ()
{
Bool Error (false);
Try
{
Value = * value_;
}
Catch (...)
{
Error = True;
}
Return Error;
}
This new interface that provides a weaker promise is safe. Is it safe? Yes. The only resource owned by Wrapper is the memory assigned to * Value_, and it is protected, even if Operator = throws an exception.
In accordance with the initial instructions, get () has a robust unusual security commitment, even if T has no promise. In the end, we have enhanced the commitment of get () (depending on the value), and it should reduce it to the commitment level of T. We use a warning to correct the commitment of get (), based on the state we cannot control or not predict T. In the end, we over-committed get's guarantee (the determinism of value), and had to bring it down to T's level We amended get's contract with a caveat, based on conditions in T we could not control or predict principles: The robustness of the program is equal to its weakest commitment. Provide the most robust commitment as much as possible while in behavior and interface.
Introduction: If your own interface is committed to the interface of others, you usually have to weaken your interface to the extent of match.
1.2 Problem # 3: SET
Our current X :: set () is:
Void Set (T Const & Value)
{
* Value_ = Value;
}
(Unlike Get (), set () does modify the Wrapper object, so it cannot be declared as cosnt.)
Statement
* Value_ = Value;
It should be very familiar: she is just the statement mentioned in the previous Problem # 2
Value = * value_;
The reverse order. Note that this change is the same as Problem # 2: Bool Set (T Const & Value) Throw ()
{
Bool Error (false);
Try
{
* Value = value_;
}
Catch (...)
{
Error = True;
}
Return Error;
}
As we encountered in get () in get (): If Operator = throws an exception, we can't know the status of * Value_. Our warnings for get ()'s commitment are equally applicable here.
Get () and set () now have the same operation but different uses: GET () assigns the value of the current object to another object, and set () assigns the value of another object to the current object. Due to this symmetry, we can put a common code into an assign () function:
Static Bool Assign (T & T, T Const & from) throw ()
{
Bool Error (false);
Try
{
TO = from;
}
Catch (...)
{
Error = True;
}
Return Error;
}
After using this auxiliary function, get () and set () are shortened to
Bool Get (T & Value) Const Throw ()
{
Return Assign (Value, * Value_);
}
Bool Set (T Const & Value) Throw ()
{
Return Assign (* Value_, Value);
}
1.3 final version
The final version of Wrapper is
Template
Class Wrapper
{
PUBLIC:
Wrapper () throw ()
: value_ (NULL)
{
Try
{
Value_ = new t;
}
Catch (...)
{
}
}
~ wrapper () throw ()
{
Try
{
DELETE VALUE_;
}
Catch (...)
{
Operator delete (Value_);
}
}
Bool Get (T & Value) Const Throw ()
{
Return Assign (Value, * Value_);
}
Bool Set (T Const & Value) Throw ()
{
Return Assign (* Value_, Value);
}
Private:
Bool Assign (T & T, T Const & from) throw ()
{
Bool Error (false);
Try
{
TO = from;
}
Catch (...)
{
Error = True;
}
Return Error;
}
T * value_;
Wrapper (Wrapper Const &);
Wrapper & Operator = (Wrapper Const &);
}
(Wow! 52 lines, only 20 lines! And this is just a simple example.)
Note that all exception handle functions just absorb those exceptions without doing any processing. Although this makes Wrapper unusual security, these exceptions have been made without records.
The principle of conflict in the constructor in the part13 is equally applicable here. Abnormal security is not enough, and it is actually not reached the expected purpose, if it masks the original abnormal state. At the same time, if the abnormal object killed the program before being captured, most of the abnormal recovery scenarios will fall. Finally, a good design must meet the following two principles:
l At the abnormal state by the presence of an abnormal object, the reaction is made appropriately.
l Make sure that creation and dissemination of abnormal objects will not cause greater damage. (Don't let the treatment behavior is even worse than the disease itself.)
1.4 other statement
In the past 3, I analyzed an abnormal safety. I strongly recommend that you read these articles:
l The first principles of C exception safety come from Tom Cargill's "Exception Handling: A False Sense of Security,". originally published in the November and December 1994 issues of C Report This article, more than any other, alerted us to the true complexities And SubtleTies of C Exception Handling.
l C Godfather Bjarne Stroustrup is writing an exception-safety Appendix for his book The C Programming Language (http://www.research.att.com/~bs/3rd.html) (Third Edition). Bjarne's offering a draft version ( Httt.com/~bs/3rd_safe0.html) of That Chapter on The Internet.
l I tend to think of exception safety in terms of contracts and guarantees, ideas formalized in Bertrand Meyer's "Design by Contract" (http://www.eiffel.com/doc/manuals/technology/contract/page.html) programming philosophy . Bertrand realizes this philosophy in both his seminal tome Object-Oriented Software Construction (http://www.eiffel.com/doc/oosc.html) and his programming language Eiffel (http://www.eiffel.com/eiffel/ Page.html) .l Herb Sutter Has Written The MOST Thorough C Exception-Safety Treatise I've Seen. He's Published It As Items 8-19 of His New Book Exceptional C (http://www1.fatbrain.com/asp/ bookinfo / bookinfo.asp? theisbn = 0201615622). If you've done time on Usenet's comp.lang.c . moderated newsgroup, you've seen Herb's Guru of the Week postings. Those postings inspired the bulk of his book. Highly recommended .
l Herb's book features a forward written by Scott Meyers. Scott covers exception safety in Items 9-15 of his disturbingly popular collection More Effective C http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=020163371X ( ). If you don't have this book, you simply must acquire it; OtherWise Scott's Royalties Could Dry Up, And He'd Have to Get A Real Job Like Mine.
Scott (in his ITEM14) believes that an exception specification should not be applied to the template member, and the opposite is the opposite. The fact is that no matter how unusually stated, there is always some procedures to protect all anomalies, so as to avoid self-destructive procedures. Scott fairly pointed out that incorrect abnormal specifications will result in std :: unexpected - this is what he suggests that you avoid it; but in this series of Part11, I pointed out that UNEXPECTED is superior to unvermnible communication.