Failure to call operator=() for return value temporary
Richard Atterer
atterer@informatik.tu-muenchen.de
Mon Feb 14 06:55:00 GMT 2000
/* Hello,
I hope this is indeed a bug in GCC and not a subtle error in my code,
but I've certainly spent a huge amount of time staring at my SmartPtr
class without finding anything.
This fails both with gcc 2.95.2 19991024 and egcs-2.90.29 980515
(egcs-1.0.3 release), under Linux 2.2, optimisation on or off
(e.g. "c++ -O0 -g -Wall smartfubar.cc -o smartfubar"), with output
like this:
X(0x8058d78)
0x8058d80 SmartPtr()
0xbffff884 SmartPtr(0x8058d78)
0xbffff884 ~SmartPtr() 0x8058d78/2
Xref = 0x8058d78
0x8058d80 ~SmartPtr() 0x8058d78/1
~X(0x8058d78)
Segmentation fault (core dumped)
Note how the static SmartPtr at 0x8058d80 is initialised with the
default ctor, which assigns 0 to the ptr member - but when it is
destroyed, the ptr value has been magically overwritten without any
operator=() call. Because operator=() is not called, it cannot
increase x.smartPtr_refCount, which results in an attempt to
'delete &x' when staticXptr is destroyed.
*/
// This code Copyright 2000 Richard Atterer, released under GPL
#include <iostream>
#define DEBUG
#ifdef DEBUG
# include <cstdlib>
# include <iostream>
#endif
//______________________________________________________________________
struct LockSmartPointedStatic;
/* The version of SmartPtrBase below needs the following:
class Base : public SmartPtrBase { ... };
class Derived : public Base { ... };
in order to allow a Derived object's address to be assigned to a
SmartPtr<Base>. Its only fault is that its member must be public. */
struct SmartPtrBase {
//friend template class<X> SmartPtr<X>;
friend struct SmartPtr_lockStatic;
SmartPtrBase() throw() : smartPtr_refCount(0) { }
int smartPtr_refCount;
};
/* The version of SmartPtrBase below needs the following:
class Base : virtual public SmartPtrBase<Base> { ... };
class Derived : virtual public SmartPtrBase<Derived>, public Base { ... };
in order to allow a Derived object's address to be assigned to a
SmartPtr<Base>. */
// template<class X> class SmartPtr;
//
// template<class X>
// class SmartPtrBase {
// friend class SmartPtr<X>;
// public:
// SmartPtrBase() throw() : smartPtr_refCount(0) { }
// private:
// int smartPtr_refCount;
// };
//______________________________________________________________________
/* If static objects are accessed through smart pointers, ensure that
there are no attempts to delete them, by defining a non-static
SmartPtr_lockStatic(&object). - MUST be non-static because order of
initialisation is not guaranteed, and the lock must be inited after
the object being locked. */
struct SmartPtr_lockStatic {
SmartPtr_lockStatic(SmartPtrBase& obj) { ++obj.smartPtr_refCount; }
~SmartPtr_lockStatic() { }
};
//______________________________________________________________________
// There are no implicit conversions from/to the actual pointer.
template<class X>
class SmartPtr {
public:
typedef X element_type;
SmartPtr() throw() : ptr(0) {
cerr << this << " SmartPtr()" << endl;
}
~SmartPtr() throw() {
cerr << this << " ~SmartPtr() " << ptr << "/"
<< (ptr == 0 ? 0 : ptr->smartPtr_refCount) << endl;
decRef();
}
// init from SmartPtr<X>
SmartPtr(const SmartPtr& x) throw() : ptr(x.get()) {
cerr << this << " SmartPtr(SP " << x.get() << ")" << endl;
incRef();
}
// init from SmartPtr to other type; only works if implicit conv. possible
template<class Y> SmartPtr(const SmartPtr<Y>& y) throw() : ptr(y.get()) {
cerr << this << " SmartPtr(SP<Y> " << y.get() << ")" << endl;
incRef();
}
// init from pointer
explicit SmartPtr(X* x) throw() : ptr(x) {
cerr << this << " SmartPtr(" << x << ")" << endl;
incRef();
}
template<class Y> SmartPtr& operator=(const SmartPtr<Y>& y) throw() {
cerr << this << " SmartPtr " << ptr << " = SP<Y> " << y.get() << endl;
if (ptr != y.get()) { decRef(); ptr = y.get(); incRef(); }
return *this;
}
template<class Y> SmartPtr& operator=(Y* y) throw() {
cerr << this << " SmartPtr " << ptr << " = " << y << endl;
if (ptr != y) { decRef(); ptr = y; incRef(); }
return *this;
}
X& operator*() const throw() { return *ptr; }
X* operator->() const throw() { return ptr; }
X* get() const throw() { return ptr; }
X* release() throw() { // relinquish ownership, but never delete
# ifdef DEBUG
if (ptr != 0 && ptr->SmartPtrBase/*<X>*/::smartPtr_refCount == 0)
abort();
# endif
if (ptr != 0) --(ptr->SmartPtrBase/*<X>*/::smartPtr_refCount);
X* tmp = ptr; ptr = 0; return tmp;
}
void swap(SmartPtr& x) throw() { X* tmp = ptr; ptr = x.ptr; x.ptr = tmp; }
bool isNull() const throw() { return ptr == 0; }
private:
void incRef() throw() {
if (ptr != 0) ++(ptr->smartPtr_refCount);
}
void decRef() throw() {
# ifdef DEBUG
if (ptr != 0 && ptr->smartPtr_refCount == 0)
abort();
# endif
if (ptr != 0 && --(ptr->smartPtr_refCount) <= 0)
delete ptr;
}
X* ptr;
};
//______________________________________________________________________
struct X : SmartPtrBase {
X() { cout << "X(" << this << ")" << endl; }
~X() { cout << "~X(" << this << ")" << endl; }
char c;
} x;
typedef SmartPtr<X> Xptr;
Xptr function2() {
return Xptr(&x);
// gdb: At this point, ~Xptr is called for the return value object!
}
Xptr staticXptr;
Xptr& function1() {
staticXptr = function2();
/* gdb: At this point, x.smartPtr_refCount is _1_! (Should be 2, one
from SmartPtr_lockStatic, one from staticXptr.) Also, the
contents of the anon. function2() return value have been copied
over the contents of staticXptr. Looks like memcpy() where a
operator=() would have been needed. */
return staticXptr;
}
int main() {
SmartPtr_lockStatic ptrlock(x);
Xptr& xref(function1());
cout << "Xref = " << xref.get() << endl;
}
// Local variables:
// compile-command: "c++ -O0 -g -Wall smartfubar.cc -o smartfubar"
// End:
/*
--
__ _
|_) /| Richard Atterer (currently at Queen's University, Belfast, NI)
| \/ï| http://www.in.tum.de/~atterer/
ï ô` ï
*/
More information about the Gcc-bugs
mailing list