Re: Smart pointer referencing its owner
On Mar 6, 10:06 pm, Peter Dimov <pdi...@gmail.com> wrote:
On Feb 27, 2:00 pm, Greg Herlihy <gre...@mac.com> wrote:
On Feb 26, 4:12 pm, Pavel Minaev <int...@gmail.com> wrote:
Consider the following (simplified) implementation of
std::auto_ptr<T>::reset():
void reset(T* newptr = 0) {
if (this->ptr && this->ptr != newptr) {
delete this->ptr;
}
this->ptr = newptr;
}
Now consider the following code which uses the above implementation:
struct foo {
std::auto_ptr<foo> ap;
foo() : ap(this) {}
void reset() { ap.reset(); }
};
int main() {
(new foo)->reset();
}
With the above implementation of auto_ptr, this results in U.B. at the
point of auto_ptr::reset().
By the way, this whole scheme also fully applies to shared_ptr, and so
do the questions. That sample auto_ptr implementation is quite real,
too - at least one compiler vendor implements it that way (and does
the same to shared_ptr).
shared_ptr's reset is defined as
Effects: Equivalent to shared_ptr(p).swap(*this).
which means that the example above will work.
None of this example applies to shared_ptr because there is no chance
that calling shared_ptr::reset() in the example program could delete
the object itself, because in order to access the object (and its
shared_ptr member) in the first place, the program must have obtained
the object's pointer from some -other- shared_ptr instance still
extant in the program.
In fact, if the only pointer for a shared object is held by the object
itself, then the object will "leak" memory. Similarly, if the only
other pointers are held by weak_ptrs, then the object does not leak
memory - but wastes memory. Note that even in the latter case, the
program must first still obtain a shared_ptr from a weak_ptr before it
can manipulate the shared object and its shared_ptr member.
So the natural conclusion to draw from these examples is that objects
should not hold shared_ptr's to themselves.
The sample code really has nothing to do with the behavior of auto_ptr
or shared_ptr.
It really does. Here's an auto_ptr::reset that doesn't suffer from the
above problem:
void reset( T* newptr = 0 )
{
std::swap( this->ptr, newptr );
if( newptr && this->ptr != newptr )
{
delete newptr;
}
}
If the auto_ptr is a member of the object being managed, then the
above implementation of auto_ptr::reset() does no better at fulfilling
the required postcondition any better than the original
implementation. In fact, it is not possible for any implementation of
reset() to meet the required postcondition in the case that the
auto_ptr deletes itself.
Therefore, the only logical conclusion is that ensuring that an
auto_ptr does not delete itself - is an (undocumented) precondition of
auto_ptr::reset(). And since the sample program did not meet this
necessary precondition before calling reset(), the behavior of the
function call itself - is undefined.
Generally, undefined behavior that results in catastrophic, or "hard"
failures (such as infinite recursion) is better than undefined
behavior that fails in silent and subtle ways - because the undefined
behavior in the former case is much more likely to be detected and
fixed.
Greg
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]