Topic: private delete operator legal?


Author: jbarfurth@vossnet.de (Joerg Barfurth)
Date: 1999/11/27
Raw View
Tyge L=F8vset <Tyge.Lovset@cmr.no> wrote:

> Thanks for clearifying, but I am still somewhat in the dark.

> Hmm. In that case, is the following code conforming?
Basically yes, afaics. But ... (see below)

> struct smart_tag {smart_tag() {}}; const smart_tag smart;
>=20
> template <class Alloc =3D alloc>
> class smart_object {
>    public:
>       typedef Alloc allocator_type;
>       smart_object() : m_refs(0) {}
>       smart_object(const smart_object&) : m_refs(0) {}
>       smart_object& operator=3D(const smart_object&)
>          {return *this;} // retain value of m_refs.
>       virtual ~smart_object() {}
>       void* operator new(size_t size, const smart_tag&)
>          {return Alloc::allocate(size);}
>       void* operator new(size_t, void* ptr) { return ptr; }
>       void operator delete(void* ptr, const smart_tag&) {} // <-- match=
 1
>       void operator delete(void* ptr, void*) {} // <-- match 2
>    private:
>       mutable int m_refs;
>       void operator delete(void* ptr, size_t sz) {Alloc::deallocate(ptr=
,
> sz);}
>       template <class X> friend class smart_ptr; // calls delete above
>       void smart_ptr_incr() const { ++m_refs; }
>       int  smart_ptr_decr() const { return --m_refs; }
> };

You should call Alloc::deallocate or your private operator delete from
operator delete(void*, const smart_tag&). Otherwise the memory allocated
will not be deallocated, if initialization throws an exception.
As Alloc::deallocate seems to need the size allocated, you may have some
trouble here :-(
=20
Consider:
    struct A : smart_object<>
    {
        A() { throw "No way"; }
        A(int) {}
    };
    int foo() { throw 42; }
Now:
    new(smart) A;        // will leak
    new(smart) A(foo()); // may leak

As others have suggested you also should make the 'structors of
smart_object protected, as a naked smart_object won't be very useful.

-- J=F6rg Barfurth
btw: I'd simply make the copy-c'tor and -assignment private and
unimplemented. This way you can easily prevent unwanted slicing. If a
derived class wants to allow copying, the copy c'tor would be written to
call smart_object's default c'tor, while the copy assignment operator
needn't call the base class at all. For ref-counted objects I usually
find it to be more up to their semantics to have assign(), copy() or
clone() members to replicate values. Of course this depends on what kind
of objects you want to derive from smart_object.

 =20
---
[ 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/24
Raw View
Thanks for clearifying, but I am still somewhat in the dark.

Joerg Barfurth wrote:

> Tyge L   vset <Tyge.Lovset@cmr.no> wrote:
>
> > Hi,
> > I am writing a smart pointer class and want to disable the user
> > to delete objects manualy, i.e the smart pointer should do it
> > automatically using reference counting.
> Nothing prevents the user from allocating such objects with
> T* p = ::new T and deallocating them using ::delete(p)

This is OK. In this case I explicitly say that I am going to
deallocate p myself, and only use regular pointers to T.

> Note however, that having a private operator delete and a public
> operator new (or no class-specific operator new at all) is a mismatch.
>
> When allocating an object with a new expression, the corresponding
> operator delete must be accessible so that it can be called in case the
> new-expression fails with an exception.
> So a function that allocates a reference counted object must have access
> to the private members of the class where the deallocation function is
> declared.

Hmm. In that case, is the following code conforming?

struct smart_tag {smart_tag() {}}; const smart_tag smart;

template <class Alloc = alloc>
class smart_object {
   public:
      typedef Alloc allocator_type;
      smart_object() : m_refs(0) {}
      smart_object(const smart_object&) : m_refs(0) {}
      smart_object& operator=(const smart_object&)
         {return *this;} // retain value of m_refs.
      virtual ~smart_object() {}
      void* operator new(size_t size, const smart_tag&)
         {return Alloc::allocate(size);}
      void* operator new(size_t, void* ptr) { return ptr; }
      void operator delete(void* ptr, const smart_tag&) {} // <-- match 1
      void operator delete(void* ptr, void*) {} // <-- match 2
   private:
      mutable int m_refs;
      void operator delete(void* ptr, size_t sz) {Alloc::deallocate(ptr,
sz);}
      template <class X> friend class smart_ptr; // calls delete above
      void smart_ptr_incr() const { ++m_refs; }
      int  smart_ptr_decr() const { return --m_refs; }
};


> I'm not sure though, if such a new expression should actually be
> rejected by the compiler.
>
> IMO the standard is not completely unambiguous on this: In clause 5.3.4
> [expr.new] we find:
>
> p.16: "If the new-expression creates an object [...] of class type,
> access and ambiguity control are done for [...] the deallocation
> function (12.5) [...]"
>
> p.17: If any part of the object initialization described above
> terminates by throwing an exception and a suitable deallocation function
> can be found, the deallocation function is called [...]
> If no unambiguous matching deallocation function can be found,
> propagating the exception does not cause the object's memory to be
> freed.
>
> Here p.17 suggests that ambiguous matching deallocation functions do not
> prevent the corresponding new expression from being well formed.
> By analogy one could assume, that an inaccessible deallocation function
> would just not be considered 'not found'.
> With this interpretation an exception would only cause the allocated
> memory to be leaked.
>
> Moreover p.16 doesn't mention that there may not be a matching
> deallocation function for which access and ambiguity control could be
> done. So it might be understood, that this check should only be applied
> to decide whether to call a deallocation function or not.
>
> The compiler might generate for
>     T* p = new T(arg);
> something like:
>     T* p;
>     void* raw = T::operator new(sizeof(T));
>     try
>     {
>         p = ::new(raw) T(arg);  // standard library placement new
>     }
>     catch (...)
>     {
>     #if (deallocation function available accessibe and unambiguous)
>         T::operator delete(raw);
>     #endif
>         throw;
>     }
> where the #if/#endif part correspond to my reading of the above.
>
> Also if I know that the constructor wont throw (and take precautions
> that the evaluation of arg doesn't either) I might just not care if
> operator delete wouldn't be called.
>
> -- 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              ]

--
Tyge L   vset                            phone    +47 55 57 43 44
Christian Michelsen Research AS  \o_   fax      +47 55 57 40 41
P.O.Box 6031, N-5020 BERGEN       |    private  +47 55 32 00 65
http://www.cmr.no                / \   mobile   +47 91 55 88 51






Author: jbarfurth@vossnet.de (Joerg Barfurth)
Date: 1999/11/19
Raw View
Tyge L=F8vset <Tyge.Lovset@cmr.no> wrote:

> Hi,
> I am writing a smart pointer class and want to disable the user
> to delete objects manualy, i.e the smart pointer should do it
> automatically using reference counting.
Nothing prevents the user from allocating such objects with=20
T* p =3D ::new T and deallocating them using ::delete(p)
>=20
> GCC 2.95 and most other compilers does not accept
> private declaration of the delete operator.
This is not conforming to the standard.

> However, SGI MipsPro 7.30 (latest) accepts it, but only
> if I also define a new operator with extra argument(s)!
Nor is this.

Note however, that having a private operator delete and a public
operator new (or no class-specific operator new at all) is a mismatch.

When allocating an object with a new expression, the corresponding
operator delete must be accessible so that it can be called in case the
new-expression fails with an exception.
So a function that allocates a reference counted object must have access
to the private members of the class where the deallocation function is
declared.

I'm not sure though, if such a new expression should actually be
rejected by the compiler.=20

IMO the standard is not completely unambiguous on this: In clause 5.3.4
[expr.new] we find:

p.16: "If the new-expression creates an object [...] of class type,
access and ambiguity control are done for [...] the deallocation
function (12.5) [...]"

p.17: If any part of the object initialization described above
terminates by throwing an exception and a suitable deallocation function
can be found, the deallocation function is called [...]=20
If no unambiguous matching deallocation function can be found,
propagating the exception does not cause the object's memory to be
freed.

Here p.17 suggests that ambiguous matching deallocation functions do not
prevent the corresponding new expression from being well formed.=20
By analogy one could assume, that an inaccessible deallocation function
would just not be considered 'not found'.=20
With this interpretation an exception would only cause the allocated
memory to be leaked.=20

Moreover p.16 doesn't mention that there may not be a matching
deallocation function for which access and ambiguity control could be
done. So it might be understood, that this check should only be applied
to decide whether to call a deallocation function or not.

The compiler might generate for
    T* p =3D new T(arg);
something like:
    T* p;
    void* raw =3D T::operator new(sizeof(T));
    try
    {
        p =3D ::new(raw) T(arg);  // standard library placement new
    }
    catch (...)
    {
    #if (deallocation function available accessibe and unambiguous)
        T::operator delete(raw);
    #endif
        throw;
    }
where the #if/#endif part correspond to my reading of the above.

Also if I know that the constructor wont throw (and take precautions
that the evaluation of arg doesn't either) I might just not care if
operator delete wouldn't be called.

-- 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: Steve Clamage <stephen.clamage@sun.com>
Date: 1999/11/19
Raw View
Tyge L=F8vset wrote:
> =

> I am writing a smart pointer class and want to disable the user
> to delete objects manualy, i.e the smart pointer should do it
> automatically using reference counting.
> =

> GCC 2.95 and most other compilers does not accept
> private declaration of the delete operator.
> =

> However, SGI MipsPro 7.30 (latest) accepts it, but only
> if I also define a new operator with extra argument(s)!

In principle, an operator delete can be private, but it will
probably prevent you using the class the way you want. When
you allocate an object using a class-specific operator new,
the matching operator delete must be accessible at the point
where the object is created.

The reason for the rule:
 T* p =3D new T;
If the T constructor exits via an exception, the compiler must
call the matching operator delete for you, since you cannot tell
whether it was the allocation or the construction that failed.

When you created an operator delete with extra arguments, it
was no longer the matching operator delete.

Probably you want something like a factory pattern, a simple
version of which is to have static member functions that create
and destroy heap objects of the type. Then the operator new
and operator delete can be both be private.

-- =

Steve Clamage, stephen.clamage@sun.com
---
[ 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/17
Raw View
Hi,
I am writing a smart pointer class and want to disable the user
to delete objects manualy, i.e the smart pointer should do it
automatically using reference counting.

GCC 2.95 and most other compilers does not accept
private declaration of the delete operator.

However, SGI MipsPro 7.30 (latest) accepts it, but only
if I also define a new operator with extra argument(s)!

Is the SGI compiler correct here?
--
Tyge L   vset, Christian Michelsen Research AS
http://www.cmr.no



[ 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              ]