473,396 Members | 1,766 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

explicit call of constructor and destructor

Hi Everyone,

I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?

class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};

int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();
}
Dec 3 '07 #1
12 7156
On Dec 3, 9:55 pm, Rahul <sam_...@yahoo.co.inwrote:
I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?

class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}

};

int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();

Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer. The placement new can be considered as being an explicit call
to the constructor as that is what it actually does.
Dec 3 '07 #2
Rahul wrote:
Hi Everyone,

I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?

class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};

int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
Correct. The line before this one invokes your constructor. What
are you trying to do here?
obj.~Trial();
This will cause undefined behaviour when your 'main' function ends.
The destructor for the local obejct 'obj' will be called again.
Why are you trying to invoke the destructor here?
}
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Dec 3 '07 #3
On Dec 3, 10:06 pm, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
On Dec 3, 9:55 pm, Rahul <sam_...@yahoo.co.inwrote:
I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?
class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};
int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();

Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer. The placement new can be considered as being an explicit call
to the constructor as that is what it actually does.
I'm sorry i didn't get ur point, could u give an example?
Dec 3 '07 #4
On Dec 3, 10:19 pm, Rahul <sam_...@yahoo.co.inwrote:
On Dec 3, 10:06 pm, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:


On Dec 3, 9:55 pm, Rahul <sam_...@yahoo.co.inwrote:
I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?
class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};
int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();
Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer. The placement new can be considered as being an explicit call
to the constructor as that is what it actually does.

I'm sorry i didn't get ur point, could u give an example?
Example:

#include<new>

class A
{
//members
};

int main()
{
//allocate buffer
char* buffer = new char[sizeof(A)];
//construct A on that memory space
A * ptrToA = ::new (buffer) A();
//destroy the object
ptrToA->~A();
//deallocate the buffer
delete[] buffer;
}

One instance where you can find placement new usage is the standard
containers. The allocator takes in the responsibility to allocate the
buffer (allocate member) and the objects are constructed over that
buffer as they are added into the container. For example, when you do
reserve on vector object, it reserves the space for N objects meaning
allocates space for N objects but does not construct them. Then when
you do push_back etc, to add elements, they are created over that
buffer. Basically, it is a technique to reduce the overhead of
repeated calls to memory allocation function. And then when done, the
vector destructor would destroy the objects calling the destructor
explicitly for all objects in it and then call the deallocate()
function of the allocator to release the memory. Hope this helps.
Dec 3 '07 #5
On Dec 3, 11:14 pm, "Alf P. Steinbach" <al...@start.nowrote:
* Abhishek Padmanabh:
On Dec 3, 9:55 pm, Rahul <sam_...@yahoo.co.inwrote:
I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?
class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};
int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();
Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer.

That's a bit too sweeping a statement, I'm afraid.

Better to say, as a novice, don't call destructors.
Where else would one call the destructor if they are not using
placement new?

The placement new can be considered as being an explicit call
to the constructor as that is what it actually does.

Uh oh. No, that's a very very dangerous misconception.

Let's first establish what an explicit constructor call is: it is any
syntactical form that suffices to call a constructor that has been
declared as "explicit", because that's what that keyword means, it
imposes a requirement to use explicit constructor calls.
The word explicit that I used was not the C++ keyword explicit. By
explicit, I simply meant it is not implicit. We have a placement new
syntax to do that. Not as the OP has called the constructor, not if
the constructor is declared explicit or not, but when you call
placement new. Had the OP's syntax been allowed, that would be an
explicit call to the constructor. Sorry, if the "explicit constructor
call" sounded confusing.
Unfortunately many folks are completely unable to grasp both the
"explicit" and the "call" part of that, and for them I have had to use
the fallacy of authority argument, showing articles by Bjarne Stroustrup
and Andrew Koenig etc. that use this terminology. Not surprisingly,
they respond better to the fallacious authority argument than to logic.
However, if you're one such person, do the googling yourself, please.
I am not sure I understand what you are suggesting to google for. :)
Now, in placement new you supply constructor arguments that are
/forwarded/ to the constructor.
"Forwarding" is a better choice.
This is crucial, because the
constructor might throw an exception. The placement new expression sets
up an exception handler first of all, which has responsibility for
deallocation, before it calls the constructor for you -- the explicit
syntactical constructor call in the expression is, at the level of
execution, a wrapped call.
Sorry, this is new to me. How could the handler be set up by the
placement new call? The handler has to be in the code wrapping the
placement new call. For the example I have above, that exception
should remain uncaught. In case of an exception from A's constructor
(considering it is not as trivial as I have shown and can throw
something) any deallocation should not happen.

So the explicitness of the constructor call expression (or not) in a
placement new is irrelevant, and should not be confused with what the
keyword "explicit" requires: what is relevant for placement new is that
one particular placement new (namely the one defined in <new>) allows
you to call a constructor on existing storage.
Yes, that is what I meant.
Dec 4 '07 #6
On Dec 3, 7:14 pm, "Alf P. Steinbach" <al...@start.nowrote:
* Abhishek Padmanabh:
On Dec 3, 9:55 pm, Rahul <sam_...@yahoo.co.inwrote:
I have the following code and i'm able to invoke the destructor
explicitly but not the constructor. and i get a compile time error
when i invoke the constructor, why is this so?
class Trial
{
public: Trial()
{
printf("Trial\n");
}
~Trial()
{
printf("~Trial\n");
}
};
int main()
{
Trial obj;
obj.Trial(); // Causes compile time error
obj.~Trial();
Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer.
That's a bit too sweeping a statement, I'm afraid.
Better to say, as a novice, don't call destructors.
Which is even more sweeping, no?
The placement new can be considered as being an explicit call
to the constructor as that is what it actually does.
Uh oh. No, that's a very very dangerous misconception.
Let's first establish what an explicit constructor call is: it is any
syntactical form that suffices to call a constructor that has been
declared as "explicit", because that's what that keyword means, it
imposes a requirement to use explicit constructor calls.
Woah. That's not what the standard says. What the standard
says is that a constructor declared as explicit cannot be used
for implicit conversions. It then goes on to say more about
some additional cases, e.g. explicit default constructors,
explicit copy constructors, etc.. A naïve reader might be
confused, and ask what type conversions have to do with these,
but since the standard also says that an expression of the form
"A()", "A(anotherA)" or "A(1,2,3)" (where A is the name of a
type) is an "explicit type conversion (functional notation)",
why not?

Now, I'll admit that I find it very hard to see where this
nomenclature comes from. I can't really see how "A()", or
"A(1,2,3)" could be a "type conversion", explicit or not,
regardless of the notation. I rather find your wording
preferable, and would like something like "explicit creation of
a temporary" even better. But the standard is the standard.
Unfortunately many folks are completely unable to grasp both
the "explicit" and the "call" part of that, and for them I
have had to use the fallacy of authority argument, showing
articles by Bjarne Stroustrup and Andrew Koenig etc. that use
this terminology. Not surprisingly, they respond better to
the fallacious authority argument than to logic. However, if
you're one such person, do the googling yourself, please.
The problem is that 1) we're not talking about logic, we're
talking about the standard---those are two radically different
things---, and 2) such expressions don't just call the
constructor, so logically, they can't strictly be considered
"explicit constructor calls". No matter what anyone says.
Now, in placement new you supply constructor arguments that
are /forwarded/ to the constructor. This is crucial, because
the constructor might throw an exception. The placement new
expression sets up an exception handler first of all, which
has responsibility for deallocation, before it calls the
constructor for you -- the explicit syntactical constructor
call in the expression is, at the level of execution, a
wrapped call.
It depends. Unless there is a corresponding placement delete,
the compiler doesn't do anything to free the memory in case of
an exception. (On the other hand, if an expression like "A()"
terminates via an exception, the compiler does free the
allocated memory. Always.)
So the explicitness of the constructor call expression (or
not) in a placement new is irrelevant, and should not be
confused with what the keyword "explicit" requires: what is
relevant for placement new is that one particular placement
new (namely the one defined in <new>) allows you to call a
constructor on existing storage.
One particular standard placement new. You could very well
define others yourself. (Can't think of why you would want to,
but there's certainly nothing forbidding it.)

What is important, of course, is that placement new is a
placement new (regardless of whether the operator new function
returns the address of some pre-existing buffer or allocates new
memory), not an "explicit constructor call". In fact, I can't
find any section in section 5 which describes an "explicit
constructor call". The only way to "call" a constructor is as
an indirect effect of some other expression or statement: a type
conversion (implicit or explicit), a declaration or a new
operator (or a throw, or when passing value parameters to a
function, or when returning a value, or ...).

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Dec 4 '07 #7
On Dec 4, 4:47 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:

[...]
This is crucial, because the
constructor might throw an exception. The placement new expression sets
up an exception handler first of all, which has responsibility for
deallocation, before it calls the constructor for you -- the explicit
syntactical constructor call in the expression is, at the level of
execution, a wrapped call.
Sorry, this is new to me. How could the handler be set up by
the placement new call?
There are four different ways the compiler can generate code for
a new expression, depending on whether there is a corresponding
(placement) delete, and whether the operator new function is
declared not to throw or not. Basically (for p = new T):

corresponding delete, operator new() may throw:

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
try {
T::constructor( tmp ) ;
} catch ( ... ) {
operator delete( tmp /* , any placement params. */ ) ;
throw ;
}

corresponding delete, operator new declared with throw()

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
if ( tmp != NULL ) {
try {
T::constructor( tmp ) ;
} catch ( ... ) {
operator delete( tmp /* , any placement params. */ ) ;
throw ;
}
}

no corresponding delete, operator new() may throw:

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
T::constructor( tmp ) ;

no corresponding delete, operator new declared with throw()

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
if ( tmp != NULL ) {
T::constructor( tmp ) ;
}

As you can see, if there is a corresponding operator delete, the
compiler must call it if the constructor terminates with an
exception.

Note that except for the case of a placement new expression with
no corresponding delete or a local static variable, the compiler
always frees the memory allocated for the variable being
constructed. Except in new expressions, however, this all
happens behind the scenes, where you cannot get at it to
instrument. (While I've shown this as try blocks in the above,
it's more likely that the compiler uses the same mechanism it
uses to call destructors of local variables in practice.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Dec 4 '07 #8
On Dec 4, 8:30 pm, James Kanze <james.ka...@gmail.comwrote:
On Dec 4, 4:47 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:

[...]
This is crucial, because the
constructor might throw an exception. The placement new expression sets
up an exception handler first of all, which has responsibility for
deallocation, before it calls the constructor for you -- the explicit
syntactical constructor call in the expression is, at the level of
execution, a wrapped call.
Sorry, this is new to me. How could the handler be set up by
the placement new call?

There are four different ways the compiler can generate code for
a new expression, depending on whether there is a corresponding
(placement) delete, and whether the operator new function is
declared not to throw or not. Basically (for p = new T):

corresponding delete, operator new() may throw:

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
try {
T::constructor( tmp ) ;
} catch ( ... ) {
operator delete( tmp /* , any placement params. */ ) ;
throw ;
}

corresponding delete, operator new declared with throw()

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
if ( tmp != NULL ) {
try {
T::constructor( tmp ) ;
} catch ( ... ) {
operator delete( tmp /* , any placement params. */ ) ;
throw ;
}
}

no corresponding delete, operator new() may throw:

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
T::constructor( tmp ) ;

no corresponding delete, operator new declared with throw()

T* tmp = operator new( sizeof(T) /* , any placement params.
*/ ) ;
if ( tmp != NULL ) {
T::constructor( tmp ) ;
}

As you can see, if there is a corresponding operator delete, the
compiler must call it if the constructor terminates with an
exception.

Note that except for the case of a placement new expression with
no corresponding delete or a local static variable, the compiler
always frees the memory allocated for the variable being
constructed. Except in new expressions, however, this all
happens behind the scenes, where you cannot get at it to
instrument. (While I've shown this as try blocks in the above,
it's more likely that the compiler uses the same mechanism it
uses to call destructors of local variables in practice.)
Ok, thanks for that. So then, how is an operator new overload (which
can be called an allocation function, right?) different from a user
defined placement new form (not using the one for in place
construction as available in <new>)?

operator new/operator delete overloads should match the signature of
their global forms provided by the compiler. And then when the objects
of the class, for which these are overloaded, are created using new
expression, use the operator new overload for allocation and the
corresponding operator delete for deallocation.

If placement allocation function and placement deallocation functions
also manage allocation and deallocation then what is different between
these and the above overloads? Just that if the signature if a direct
match with operator new/operator delete, they are overloads else they
are placement allocation functions?

Also, referring to the example as in 5.3.4 (20) as below:

struct S {
// Placement allocation function:
static void* operator new(std::size_t, std::size_t);
// Usual (non-placement) deallocation function:
static void operator delete(void*, std::size_t);
};

S* p = new (0) S; // ill-formed: non-placement deallocation function
matches
// placement allocation function

The standard also says (in 18.5.1.3 - Placement forms) that the
placement form function is reserved and a C++ program may not define
them that would displace these? Is that specific to the form as below:

void* operator new(std::size_t size , void* ptr ) throw();

and if I had a different argument list for this - would that be
allowed?
Dec 5 '07 #9
On Dec 5, 11:12 am, "Alf P. Steinbach" <al...@start.nowrote:
* Abhishek Padmanabh:
On Dec 4, 8:30 pm, James Kanze <james.ka...@gmail.comwrote:
On Dec 4, 4:47 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
[...]
Ok, thanks for that. So then, how is an operator new overload (which
can be called an allocation function, right?) different from a user
defined placement new form (not using the one for in place
construction as available in <new>)?

"user defined placement form" is AFAIK not a term employed by the
standard, nor is it a commonly used term. But assuming you mean it as a
description: a "new" expression for single object accepts two lists of
arguments,

new (arglist1) T(arglist2)

arglist1 is passed to the allocation function, so by definining an
allocation function (which unfortunately is named "operator new") you
enable a particular signature for this argument list.

arglist2 is passed to a T constructor, so by definining a constructor
you enable a particular signature for this argument list.

There's not really more to it.
Yes, this is okay. Let's go a little further and see if I got it
right.
>
operator new/operator delete overloads should match the signature of
their global forms provided by the compiler.

No.
And then when the objects
of the class, for which these are overloaded, are created using new
expression, use the operator new overload for allocation and the
corresponding operator delete for deallocation.

Huh?
What I am saying is - if the overloaded operator new has the same
signature as that of the global operator new, then I can create
objects as below:

struct A {
A(/*constructor arg list*/);
void* operator new (size_t size); //same signature
as ::operator new() //1
};

int main()
{
A * ptr = new A(/*constructor arg list*/);
}

If the argument list is changed by adding additional arguments to the
operator new overload for class A as below:

struct A {
A(/*constructor arg list*/);
void* operator new (size_t size, size_t additional_argument); //
different signature as ::operator new() //2
};
int main()
{
A * ptr = new A(/*constructor arg list*/);
}

then the above way of create object of A will fail as a match is not
found but if the statement:

A * ptr = new A(/*constructor arg list*/);

is modified to :

size_t additional_argument=0;
A * ptr = new(additional_argument) A(/*constructor arg list*/);

then the overloaded one will be called and this form of new expression
(which specifies arguments to allocation function) is called a
placement new form. Is that correct?

Also, one such overload is provided for the global allocation function
under the header <newthat does not do any allocation but just does
in place construction. Is that correct?
If placement allocation function and placement deallocation functions
also manage allocation and deallocation then what is different between
these and the above overloads? Just that if the signature if a direct
match with operator new/operator delete, they are overloads else they
are placement allocation functions?

It seems you are assuming a distinction that does not exist.
Since there is no distinction, placement new is any overload of
operator new that gets its additional arguments from the new
expression argument list. Is that correct? So, in the code I posted
above,

//1 - is an overload of operator new
//2 - is an overload of operator new but has additional arguments
and hence relates to placement new form where additional arguments
need to provided as part of arglist1 as in new (arglist1) T(arglist2)
as posted by you.
Also, referring to the example as in 5.3.4 (20) as below:
struct S {
// Placement allocation function:
static void* operator new(std::size_t, std::size_t);
// Usual (non-placement) deallocation function:
static void operator delete(void*, std::size_t);
};
S* p = new (0) S; // ill-formed: non-placement deallocation function
matches
// placement allocation function

The current standard's §5.3.4/20 does not have the above example, or any
example. However, this example is in §5.3.4/20 of the draft for C++0x.
That paragraph correspond to §5.3.4/19 in the current standard.

The rule concerning the ill-formed'ness of the above example was added
in the draft.

It's a special case that evidently wasn't considered in the 98 standard.
The standard also says (in 18.5.1.3 - Placement forms)

In the current standard this is §18.4.1.3.
that the
placement form function is reserved and a C++ program may not define
them that would displace these? Is that specific to the form as below:
void* operator new(std::size_t size , void* ptr ) throw();

No, it's specific to the two forms of operator new and two forms of
operator delete specified there, in the global namespace.
Is this the one that does in-place construction?
Dec 5 '07 #10
On Dec 4, 8:47 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
On Dec 3, 11:14 pm, "Alf P. Steinbach" <al...@start.nowrote:
* Abhishek Padmanabh:
Calling the destructor twice is undefined behavior. The only place
where explicitly calling the destructor is required and justified is
when you used placement new to do the constructor over a pre-allocated
buffer.
That's a bit too sweeping a statement, I'm afraid.
Better to say, as a novice, don't call destructors.

Where else would one call the destructor if they are not using
placement new?
This was unanswered, where else would you invoke the destructor, as in
objectOfClassA.~A(), if not in the context of in-place construction?
Dec 5 '07 #11
On Dec 5, 6:03 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
On Dec 4, 8:30 pm, James Kanze <james.ka...@gmail.comwrote:
On Dec 4, 4:47 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
[...]
Ok, thanks for that. So then, how is an operator new overload
(which can be called an allocation function, right?) different
from a user defined placement new form (not using the one for
in place construction as available in <new>)?
Attention. We're dealing with two different things here. You
can overload the global operator new function, by providing one
with a different signature, and you can replace the standard
operator new function with one of your own.

In the second case, your function must have exactly the same
signature and the same semantics as the original, including
throwing std::bad_alloc (or a class derived from it) in case of
insufficient memory. And there *is* a corresponding operator
delete function for this, so any new expression using it will
require the compiler to generate code ensuring that the operator
delete function is called if the constructor exits via an
exception.

In the first case, of course, it's up to you. You can either
report failure by means of bad_alloc, or you can declare the
function throw(), and return a null pointer. You can provide a
corresponding placement delete, which will be called if the
constructor exits via an exception, or not (in which case
nothing will be called).
operator new/operator delete overloads should match the
signature of their global forms provided by the compiler.
If the signatures match, then it's not an overload, it's a
replacement.
And then when the objects of the class, for which these are
overloaded, are created using new expression, use the operator
new overload for allocation and the corresponding operator
delete for deallocation.
If placement allocation function and placement deallocation
functions also manage allocation and deallocation then what is
different between these and the above overloads?
They use different algorithms?

The most frequent case I've seen is something like:

void* operator new( size_t n, MemoryPool& ) ;
void operator delete( void* p, MemoryPool& ) ;

Invoked by something like:

MemoryPool pool1 ;
T* p = new ( pool1 ) T ;

In this case, you probably need the placement delete, since if
the constructor fails, you want to delete the memory. (Or maybe
the pools are garbage collected in some way, and you don't need
it.)
Just that if the signature if a direct match with operator
new/operator delete,
If the signature is a direct match, they are replacements, not
overloads.
they are overloads else they are placement allocation
functions?
Also, referring to the example as in 5.3.4 (20) as below:
struct S {
// Placement allocation function:
static void* operator new(std::size_t, std::size_t);
// Usual (non-placement) deallocation function:
static void operator delete(void*, std::size_t);
};
S* p = new (0) S; // ill-formed: non-placement deallocation function
matches
// placement allocation function
This is a special case. The problem with member operator delete
is that the non-placement form can have one of two different
signatures. Which leads to an ambiguity if the placement form
of new takes a size_t as it's additional argument: is the
operator delete, above, a placement form (which should be called
if the constructor in a corresponding placement new throws), or
is it the standard form (in which case, it will only be called
if non-placement new is used).

On the whole, my comments didn't take member overloads of
operator new into consideration, but except for the fact that
the non-placement delete can have one of two different
signatures, the rules are pretty much the same. Of course, the
name lookup rules means that a placement operator new in a class
will hide the global non-placement operator, so objects of that
type can only be created by placement new.

More generally, overloading operator new and operator delete can
be tricky; it's not something for beginners.
The standard also says (in 18.5.1.3 - Placement forms) that the
placement form function is reserved and a C++ program may not define
them that would displace these? Is that specific to the form as below:
void* operator new(std::size_t size , void* ptr ) throw();
and if I had a different argument list for this - would that be
allowed?
Exactly. You are not allowed to replace this form of placement
new, since much of the standard library code probably depends on
it having exactly the semantics provided for by the standard
library.

Note that at a language level, there's nothing special about
this level. As far as the compiler is concerned, the new
expression behaves as always: it calls an allocator function to
acquire the memory, then the constructor of the object. Because
this particular placement new is declared nothrow, the compiler
will also generate code to check that the returned pointer is
not null before calling the constructor. Because the library
also declares a corresponding placement delete, the compiler
will also generate code to ensure that it is called if the
constructor exits via an exception. (I've often wondered about
this; the semantics of the corresponding placement delete are to
do nothing, so I don't see the reason behind having it, and
getting it called. Perhaps just to prevent user code from
accidentally declaring it.)

And of course, by using this "allocator", the language itself
never violates the rule that a constructor cannot be invoked
without memory allocation.

The more I think about it, the more I think that constructors
really are somewhat special. All other member functions,
including the destructor, are either static or non-static. If
they're static, there's no object, and not this pointer. If
they're non-static, they are always called on a specific object,
whose address becomes the this pointer. The constructor is
special in that it not called on an object---before the
constructor is called, the object doesn't exist---but that it
has a this pointer. So it's really a third type of member
function, not really static (since it has a this pointer), but
not really non-static either (since you don't need an object to
call it). The standard could have provided a special syntax,
say p->TypeName() (where p would have type void*, or be
convertible to a void*), to call the constructor, without any
allocation. But this would be a special syntax: the "placement
new" trick works just as well, and the operation isn't frequent
enough to warant a special syntax.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Dec 5 '07 #12
First off, thank you very much for the help so far!

On Dec 5, 5:36 pm, James Kanze <james.ka...@gmail.comwrote:
On Dec 5, 6:03 am, Abhishek Padmanabh <abhishek.padman...@gmail.com>
wrote:
operator new/operator delete overloads should match the
signature of their global forms provided by the compiler.

If the signatures match, then it's not an overload, it's a
replacement.
Yes, exactly. I think this was the basic mis-understanding I was
having. Let's see if it is gone now.
And then when the objects of the class, for which these are
overloaded, are created using new expression, use the operator
new overload for allocation and the corresponding operator
delete for deallocation.
If placement allocation function and placement deallocation
functions also manage allocation and deallocation then what is
different between these and the above overloads?

They use different algorithms?
Right, now I think what I was confused about. There are certain
overloads to operator new/operator delete that can only be called
using the placement new form/expression. These overloads are called
placement allocation/deallocation forms. I was thinking that these
overloads were different from the placement allocation/deallocation
forms which I think is wrong. Could you please see my understanding
below and confirm on those points?
The most frequent case I've seen is something like:

void* operator new( size_t n, MemoryPool& ) ;
void operator delete( void* p, MemoryPool& ) ;

Invoked by something like:

MemoryPool pool1 ;
T* p = new ( pool1 ) T ;

In this case, you probably need the placement delete, since if
the constructor fails, you want to delete the memory. (Or maybe
the pools are garbage collected in some way, and you don't need
it.)
Should it be deleting the memory? Shouldn't the MemoryPool be the one
managing it? And this operator delete doing just some pointer
adjustments? Please see below as well.
Just that if the signature if a direct match with operator
new/operator delete,

If the signature is a direct match, they are replacements, not
overloads.
they are overloads else they are placement allocation
functions?
Also, referring to the example as in 5.3.4 (20) as below:
struct S {
// Placement allocation function:
static void* operator new(std::size_t, std::size_t);
// Usual (non-placement) deallocation function:
static void operator delete(void*, std::size_t);
};
S* p = new (0) S; // ill-formed: non-placement deallocation function
matches
// placement allocation function

This is a special case. The problem with member operator delete
is that the non-placement form can have one of two different
signatures. Which leads to an ambiguity if the placement form
of new takes a size_t as it's additional argument: is the
operator delete, above, a placement form (which should be called
if the constructor in a corresponding placement new throws), or
is it the standard form (in which case, it will only be called
if non-placement new is used).

On the whole, my comments didn't take member overloads of
operator new into consideration, but except for the fact that
the non-placement delete can have one of two different
signatures, the rules are pretty much the same. Of course, the
name lookup rules means that a placement operator new in a class
will hide the global non-placement operator, so objects of that
type can only be created by placement new.
Regarding the 2 forms (and this special case), please see below.
More generally, overloading operator new and operator delete can
be tricky; it's not something for beginners.
Sorry, I know I am not an expert but that hurts. I just want to
understand it else it will keep haunting me.
The standard also says (in 18.5.1.3 - Placement forms) that the
placement form function is reserved and a C++ program may not define
them that would displace these? Is that specific to the form as below:
void* operator new(std::size_t size , void* ptr ) throw();
and if I had a different argument list for this - would that be
allowed?

Exactly. You are not allowed to replace this form of placement
new, since much of the standard library code probably depends on
it having exactly the semantics provided for by the standard
library.
Yes, these cannot be replaced but overloaded. :)
Note that at a language level, there's nothing special about
this level. As far as the compiler is concerned, the new
expression behaves as always: it calls an allocator function to
acquire the memory, then the constructor of the object. Because
this particular placement new is declared nothrow, the compiler
will also generate code to check that the returned pointer is
not null before calling the constructor. Because the library
also declares a corresponding placement delete, the compiler
will also generate code to ensure that it is called if the
constructor exits via an exception. (I've often wondered about
this; the semantics of the corresponding placement delete are to
do nothing, so I don't see the reason behind having it, and
getting it called. Perhaps just to prevent user code from
accidentally declaring it.)
Yes, it looks pretty useless but it is good to have a consistent
semantics. Here is my understanding as of now. Could you please
suggest if it looks right or wrong?

The global operator new and delete have the following signatures:

1. void* operator new(std::size_t) throw(std::bad_alloc); //single
object form throws std::bad_alloc
2. void* operator new[](std::size_t) throw(std::bad_alloc); //array
form - throws bad alloc
3. void* operator new(std::size_t size , const std::nothrow_t&)
throw(); //nothrow new
4. void* operator new[](std::size_t size , const std::nothrow_t&)
throw(); //no throw array form of new
5. void operator delete(void*) throw(); //single object form
6. void operator delete[](void*) throw(); //array form
7. void operator delete(void* , const std::nothrow_t&) throw(); //
corresponds to nothrow new
8. void operator delete[](void* , const std::nothrow_t&) throw(); //
corresponds to nothrow array form of new

To use 3 and 4 (related to 7 and 8 versions of operator delete),
placement form of new is used wherein there is an argument list
attached to new, as in A * ptr = new (nothtow) A();

Another set of overload for operator new/operator delete are provide
that are used for in-place construction. They are declared as below:

9. void* operator new(std::size_t size , void* ptr ) throw();
10. void* operator new[](std::size_t size , void* ptr ) throw();
11. void operator delete(void* ptr , void*) throw();
12. void operator delete[](void* ptr , void*) throw();

These are provided by the standard library and these can not be
replaced in the sense that no operator new/operator delete sets would
be defined by a program with the above 4 signatures (9 through 12).
These two operator new's don't do allocation and hence operator
delete's don't do any deallocation. The operator new just returns the
pointer ptr (no other action performed) and operator delete does not
perform any action. The operator delete is just a function that gets
called in case the constructor of the object being constructed on ptr
throws an exception.

These come to use when doing in-place construction as below:

char* buffer = new char[sizeof(A)];
A * ptrToA = new (buffer) A();
ptrToA->~A();
delete[] buffer;

If class provides replacements for functions 1 through 8 in their
class, they must have the same signature as shown in 1-8. And then
when that object is created via new expression or the placement new
nothrow form, these replacement versions will be called and
corresponding operator deletes will be called when the object is to be
normally destroyed or if the constructor of that class throws an
exception.

There can be other overloads to operator new/operator delete that can
only be called using placement new expression (the below can have any
more number of arguments - the argument list between the operator new
and delete must match except for the first argument). For example:

13. void* operator new(std::size_t size, MemoryPool& mempool) throw();
14. void* operator new[](std::size_t size, MemoryPool& mempool)
throw();
15. void operator delete(void* ptr, MemoryPool& mempool) throw();
16. void operator delete[](void* ptr, MemoryPool& mempool) throw();

One example way to use this is that the operator new would be checking
what portion of that pool they can utilize and make pointer
adjustments on the pool and return a pointer to the location wherein
the construction of the object can take place. Ideally, operator
delete should not deallocate the memory because it is part of the pool
and is being managed by the pool. It would work the same way as the
operator delete mentioned in 11 and 12. Maximum they can do it, using
MemoryPool's interface, can mark the area re-usable on which the
object was constructed since the destructor must have got called
before this operator delete gets called. If the operator delete is
deallocating memory then the advantage of a pool is lost since the
intent is to minimize system calls for acquiring and freeing memory.
It is not mandatory that you have this behavior but I am just thinking
from the point of view that the MemoryPool I am using is to allocate a
huge chunk of buffer at start-up and keep re-using it to create
objects on without making repeated calls to the system for memory
acquisition and freeing.

Now, coming to the special case,

17. void* operator new(std::size_t, std::size_t);
18. void operator delete(void * ptr, size_t);

This is still a problem for me. How is the operator delete a non-
placement deallocation function? The non-placement deallocation
function would have the signature matching 5 and 6. Right? Because
only 1 and 2 forms of operator new and their replacements are the only
ones that can be called by the new expression. For all the rest, the
placement new form must be used to supply those additional arguments.

If 18 is the second form of the non-placement allocation function,
(because you say there are 2 forms, one is the form in 5 and 6, I
believe this is the second one that you are pointing to), could you
please quote the standards section that says so?

Please bear with me. :) Thanks very much, once again.
Dec 5 '07 #13

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: tzach | last post by:
Call constructor on an already existing instance? I like to execute the constructor logic, including all the class member constructors, on an existing instance (with out executing the destructor)....
3
by: Jacques Labuschagne | last post by:
Hi all, Is it legal to kill an object and build a new one of the same type in its memory? class A{ int i; public: explicit A(int value): i(value){} };
6
by: Christoph Bartoschek | last post by:
Hi, gcc 3.4 rejects the following program: class T { public: T() : a(3) {} explicit T(T const & other) : a(other.a) {} private: int a;
23
by: Fabian Müller | last post by:
Hi all, my question is as follows: If have a class X and a class Y derived from X. Constructor of X is X(param1, param2) . Constructor of Y is Y(param1, ..., param4) .
8
by: trying_to_learn | last post by:
Why do we need to explicitly call the copy constructor and the operator = , for base class and member objects in composition? ....book says "You must explicitly call the GameBoard copy-constructor...
20
by: frs | last post by:
For memory economization, I need to get rid if the virtual destructor. Under this constraint I get caught up in a design, where I need to call a destructor of a derived class from a base class....
9
by: Rennie deGraaf | last post by:
I'm writing a class similar to this: #include <iostream> using namespace std; template<class t> class Foo { private: class Bar {
7
by: dragoncoder | last post by:
Hello experts, I have the following code me. =cat mystring.h #include<iostream> using namespace std; class mystring {
7
by: michael | last post by:
Hi All, I have written the following to illustrate a problem. I know I have some magic numbers etc please ignore them. What I do not follow is why the line marked results in a call to the...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.