Topic: private delete operator? - with example.


Author: jbarfurth@vossnet.de (Joerg Barfurth)
Date: 1999/11/27
Raw View
AllanW <allan_w@my-deja.com> wrote:

> In article <38340609.4C3CD6EA@cmr.no>, someone wrote:
> ...
> > My intent was to make it impossible for users
> > to delete objects derived from smart_object;
> > The friend class smart_ptr should do it automatically:
>=20
> [Snip code using reference counts, trying to use
>  public operator new and private operator delete]
>=20
> You are confusing operator delete with the destructor.
> Try this instead: (Warning: Untested code, but this should
> give you the general idea)
 [snipped smart object without any (!) destructor]

> I removed the template<> stuff; it looked like the idea was
> to use private operator delete to prevent users from deleting
> it. Making the destructor private is the way to go, and now

You forgot the destructor, therefore the compiler declares one: public
and non-virtual. Rather bad for a base class that is supposed to be used
for deleting thru a pointer to it ...
Also: a private destructor won't help you in a base class - you cannot
derive from the class any more :-(
But if you make the destructor protected, derived classes can freely
delete smart objects. Moreover, if a derived class doesn't declare a
destructor the compiler will declare a public one....

So generally the only way to inhibit deleting a heap object derived from
a specific class, is a private operator delete.

-- J=F6rg Barfurth
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jbarfurth@vossnet.de (Joerg Barfurth)
Date: 1999/11/24
Raw View
       <jack_reeves@ibm.net> wrote:

> to be able to allocate stack objects, or if the derived class accidentally
> makes its destructor public, then all bets are off.
Sadly enough, if a derived class doesn't declare a destructor, the
compiler will do it 'accidentally' - and public.
Personally I have classes without destructor quite ofte - I try to build
a library of primitives that take care of safely copying and destructing
a single resource each. More complex classes then are composed from them
and often don't need to declare copy constructor, copy assignment or
destructor.

> I do not claim to be one of the language lawyers, but my understanding of
> visibility rules
> would say that what you are trying to do will not work anyway. Operator
> delete has to be
> visible to the derived class' destructor, since it is in fact the derived
> class' virtual
> destructor that calls operator delete. Ordinary visibility rules may not
> apply to in this case.

They don't. Access control is done on the static type for
delete-expressions.


-- J   rg Barfurth


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW <allan_w@my-deja.com>
Date: 1999/11/23
Raw View
In article <38340609.4C3CD6EA@cmr.no>, someone wrote:
...
> My intent was to make it impossible for users
> to delete objects derived from smart_object;
> The friend class smart_ptr should do it automatically:

[Snip code using reference counts, trying to use
 public operator new and private operator delete]

You are confusing operator delete with the destructor.
Try this instead: (Warning: Untested code, but this should
give you the general idea)

    // Base class for objects pointed to by smart_ptr
    class smart_object {
        /**** Assumptions: Not an ABC, but this class is
              generally not very useful by itself.
              Suggested use: base class for objects
              pointed to by a smart_ptr ****/

    private:

        // The reference count. When it goes to 0, delete it.
        mutable int smartCount__;

        // Private functions to manipulate the reference count
        void addRef__() { ++smartCount__; }
        void removeRef__() { if (!--smartCount__) delete this; }

        // Here's someone who can manipulate the reference count
        template<class T>friend class smart_ptr<T>;

    public:

        // Default constructor
        smart_object() : smartCount__(0) {}

        // Copy constructor
        smart_object(const smart_object&) : smartCount__(0) {}

        // Operator= doesn't copy smartCount__
        smart_object &operator=(const smart_object&) { return *this; }

    };

I removed the template<> stuff; it looked like the idea was
to use private operator delete to prevent users from deleting
it. Making the destructor private is the way to go, and now
we can use the usual operator new. (If you have some other
reason to use template class Alloc, please go ahead and put
it back in.)

    template<class T>class smart_ptr {
        /**** Assumptions: class T has accessible
              reference-count semantics identical to
              class smart_object. Suggested
              implementation: derive T from class
              smart_object. ****/

    private:
        // Point to the underlying object
        T*ptr;

    public:

        // Default constructor,
        // also construct from *smart_object
        smart_ptr(T*x=0) : ptr(x) { if (ptr) ptr->addRef__(); }

        // Copy constructor, implement reference counting
        smart_ptr(const smart_ptr&x) : ptr(x.ptr)
            { if (ptr) ptr->addRef__(); }

        // Destructor, implement reference counting
        ~smart_ptr() { if (ptr) ptr->removeRef__(); }

        // Assignment, implement reference counting
        smart_ptr &operator=(const smart_ptr &x)
            // Leave in this order! (self-assign works correctly)
            { if (x.ptr) x.ptr->addRef__();
              if (  ptr)   ptr->removeRef__();
              ptr = x.ptr;
              return *this;
            }

        // Is the pointer null?
        operator bool() const { return ptr; }

        // Access the underlying data for anything but delete
        operator T*() { return ptr; }
    };

By not requiring a modified new op, the usage is:

    class myClass : public smart_object { /* ... */ };
    smart_ptr<myClass>p = new myClass;

which is even neater -- there's nothing to remember about
the new-expression.

--
Allan_W@my-deja.com is a "Spam Magnet," never read.
Please reply in newsgroups only, sorry.


Sent via Deja.com http://www.deja.com/
Before you buy.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: " " <jack_reeves@ibm.net>
Date: 1999/11/22
Raw View
Having done this before, I would recommend that a simpler approach is to
make the destructor of 'smart_object', and all derived classes, "protected".
This will serve the same purpose -- no one can write
  delete ptr;
on one of your smart objects. This does require some cooperation from the
developers of
derived classes. It also means you must always allocate the objects on the
heap. If you need
to be able to allocate stack objects, or if the derived class accidentally
makes its destructor public, then all bets are off.

I do not claim to be one of the language lawyers, but my understanding of
visibility rules
would say that what you are trying to do will not work anyway. Operator
delete has to be
visible to the derived class' destructor, since it is in fact the derived
class' virtual
destructor that calls operator delete. Ordinary visibility rules may not
apply to in this
case. If they do, you can probably achieve a similar desired result by just
making
operator delete protected.


Tyge L   vset <Tyge.Lovset@cmr.no> wrote in message
news:38340609.4C3CD6EA@cmr.no...
This is a more elaborate repost of the problem I found
when I was writing a smart pointer class.
I realized that none of the compilers I tried,
allowed me to make the delete operator private.

My intent was to make it impossible for users
to delete objects derived from smart_object;
The friend class smart_ptr should do it automatically:

// Base class for objects pointed to by smart_ptr
template <class Alloc = alloc>
class smart_object {
  public:
    typedef Alloc allocator_type;
    smart_object() : smartcount__(0) {}
    smart_object(const smart_object&) : smartcount__(0) {}
    smart_object& operator=(const smart_object&)
      {return *this;} // supresses copying smartcount__
    virtual ~smart_object() {}
    void* operator new(size_t size)
      { return Alloc::allocate(size); }
    void* operator new(size_t, void* ptr)
      { return ptr; } // placement
  private:
    template <class X> friend class smart_ptr; // does the delete.
    void operator delete(void* ptr, size_t sz) // <---- oh no!
      { Alloc::deallocate(ptr, sz); }
    mutable int smartcount__;
};

template <class X> class smart_ptr {
    X* ptr;
  public:
    // ... the usual stuff, similar to auto_ptr + ref counting
    ~smart_ptr() {if (ptr && --ptr->smartcount__ == 0) delete ptr;}
    operator bool() const {return ptr;}
};

Modifying the new operator of smart_object, suddenly
makes the code OK on the SGI MipsPro 7.30 compiler:

   void* operator new(size_t size, const smart_arg&)
     { return Alloc::allocate(size); }
and:
   struct smart_arg {smart_arg(){}};
   const smart_arg smart;

It is quite strange why this change fixes my problem.
If this is correct compiler behavior, can anyone explain?
In any case: why cant we privatize the delete operator?
This example surely demonstrates that it should be allowed.

PS: With the modified new op, the usage is:
   class MyClass : public smart_object<> { /*...*/ };
   smart_ptr<MyClass> p = new (smart) MyClass;

which is actually quite neat!

smart_ptr will not delete circular and mutual referenced objects;
such chains must be broken manually, or by using some weak(regular) ptrs.
--
Tyge L   vset, Christian Michelsen Research AS
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Tyge =?iso-8859-1?Q?L=F8vset?= <Tyge.Lovset@cmr.no>
Date: 1999/11/19
Raw View
This is a more elaborate repost of the problem I found
when I was writing a smart pointer class.
I realized that none of the compilers I tried,
allowed me to make the delete operator private.

My intent was to make it impossible for users
to delete objects derived from smart_object;
The friend class smart_ptr should do it automatically:

// Base class for objects pointed to by smart_ptr
template <class Alloc =3D alloc>
class smart_object {
  public:
    typedef Alloc allocator_type;
    smart_object() : smartcount__(0) {}
    smart_object(const smart_object&) : smartcount__(0) {}
    smart_object& operator=3D(const smart_object&)
      {return *this;} // supresses copying smartcount__
    virtual ~smart_object() {}
    void* operator new(size_t size)
      { return Alloc::allocate(size); }
    void* operator new(size_t, void* ptr)
      { return ptr; } // placement
  private:
    template <class X> friend class smart_ptr; // does the delete.
    void operator delete(void* ptr, size_t sz) // <---- oh no!
      { Alloc::deallocate(ptr, sz); }
    mutable int smartcount__;
};

template <class X> class smart_ptr {
    X* ptr;
  public:
    // ... the usual stuff, similar to auto_ptr + ref counting
    ~smart_ptr() {if (ptr && --ptr->smartcount__ =3D=3D 0) delete ptr;}
    operator bool() const {return ptr;}
};

Modifying the new operator of smart_object, suddenly
makes the code OK on the SGI MipsPro 7.30 compiler:

   void* operator new(size_t size, const smart_arg&)
     { return Alloc::allocate(size); }
and:
   struct smart_arg {smart_arg(){}};
   const smart_arg smart;

It is quite strange why this change fixes my problem.
If this is correct compiler behavior, can anyone explain?
In any case: why cant we privatize the delete operator?
This example surely demonstrates that it should be allowed.

PS: With the modified new op, the usage is:
   class MyClass : public smart_object<> { /*...*/ };
   smart_ptr<MyClass> p =3D new (smart) MyClass;

which is actually quite neat!

smart_ptr will not delete circular and mutual referenced objects;
such chains must be broken manually, or by using some weak(regular) ptrs.
--
Tyge L=F8vset, Christian Michelsen Research AS
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]