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