Re: A subtle access issue (may be advanced :-) )
On 29 ao=FBt, 09:11, "Alf P. Steinbach" <al...@start.no> wrote:
The following code compiles as-is with g++ and Comeau Online, but not whe=
n then
the commented lines are uncommented:
<code>
#include <stddef.h>
template< class T > T* create();
class Base
{
template< class T > friend T* create();
private:
static void* operator new( size_t size )
{
return ::operator new( size );
}
// static void operator delete( void* p )
// {
// ::operator delete( p );
// }
protected:
virtual ~Base() {}
};
class Derived
: public Base
{
public:
Derived() {}
virtual ~Derived() {}
};
template< class T >
T* create() { return new T; }
int main()
{
create<Derived>();}
</code>
With uncommenting the commented code both compilers complain that the Der=
ived
destructor can't access Base::operator delete.
They don't complain that the Derived constructor can't access Base::opera=
tor new.
I can understand the lack of complaint for the constructor: the construct=
or
doesn't need to access the allocation function, which is invoked by the n=
ew
expression which is in the context of the create function which has acces=
s.
I don't understand the complaint for the destructor. The new expression h=
as to
potentially deallocate, if an expression is thrown. But it manages well t=
o use
the allocation function, so why can't it also use the deallocation functi=
on?
In short, why this different treatment?
It almost seems as if the standard supports an implementation technique w=
here
the call to the deallocation function is made directly from *within* the =
most
derived class' destructor?
yes, this is exactly for this reason. From the standard, the only
reference I can see is the first sentence in the section of delete (c+
+98 =A75.3.5) "The delete-expression operator destroys a most derived
object or array created by a new-expression". There is no motivation
for this statement in the standard, but you can find the explanation
in TCPL3 (SE) in =A715.6 (Free Store).
The operator delete must know the size of the object to destroy. Since
the delete operator is always static and it's first argument is of
type void*, the two solutions to know the object's size are either the
operator delete is called from the destructor of the most derived
class where the exact object's size is known and passed (e.g.
Base::operator delete(p, sizeof(Derived))), either the size is stored
with the object (or elsewhere) and the delete operator knows how to
retrieve it (e.g. malloc-like implementation). In fact these two
approach are equivalent and depend on the implementation which is
unspecified by the standard. This explains why some implementation
require the access to the operator delete of Base from the destructor
of Derived (i.e. called within ~Derived()) and some don't (called
outside ~Derived()). Since the former is more efficient in both space
and speed, it tends to be the common behavior of decent compilers.
BTW, why don't you declare Base::operator delete as protected instead
of private since its role is more or less the same as the destructor
~Base() ?
Cheers,
ld.