explicit call of constructor and destructor

R

Rahul

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();
}
 
A

Abhishek Padmanabh

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.
 
V

Victor Bazarov

Rahul said:
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
 
R

Rahul

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?
 
A

Abhishek Padmanabh

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.
 
A

Alf P. Steinbach

* 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.


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.

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.

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.

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.

Cheers, & hth.,

- Alf
 
A

Abhishek Padmanabh

* Abhishek Padmanabh:


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?

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.
 
A

Alf P. Steinbach

* Abhishek Padmanabh:
Sorry, this is new to me. How could the handler be set up by the
placement new call?

The C++ source code is translated by a compiler to executable code.

The handler has to be in the code wrapping the
placement new call.

I'm sorry, that statement is not meaningful to me.

For the example I have above, that exception
should remain uncaught.

Well, since the example code is not valid C++ code it's pretty much
irrelevant what it "should" do. :)

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.

A placement new expression is the only construct in C++ that calls
placement deallocation function.

What's new to you is possibly that placement new is a more general
construct than just "in place construction".

But this is a corner of the language that many are unfamiliar with.
Even professionals. For example, one of the most infamous bugs in
Microsoft's MFC library was a memory leak in debug builds resulting from
failure to define a placement deallocation function: they #define'd
'new' as a placement new expression using a placement allocation
function, with no corresponding placement deallocation function.

Cheers, & hth.,

- Alf
 
J

James Kanze

* Abhishek Padmanabh:
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?
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 ...).
 
J

James Kanze

On Dec 4, 4:47 am, Abhishek Padmanabh <[email protected]>
wrote:

[...]
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.)
 
A

Alf P. Steinbach

* James Kanze:
Which is even more sweeping, no?

No. :)


Woah. That's not what the standard says.

The standard doesn't say.

And in particular, it doesn't say anything contradictory.

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.

The standard doesn't define the terminology. It uses a number of
different terms, which the reader is assumed to already be familiar with
or understand as descriptive. Those terms include "explicit constructor
call", "explicit initialization" (a whole section called that), etc.

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.

It seems we've had this debate before.

So just summing up: (1) yes, the standard uses the term "explicit
constructor call" (but does not define it), and (2) yes, the language's
creator, C++ standardization committee secretary etc. uses the term in
published articles.


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 [deallocation,
function] the compiler doesn't do anything to free the memory in case of
an exception.

Yes, that's right.

(On the other hand, if an expression like "A()"
terminates via an exception, the compiler does free the
allocated memory. Always.)


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.)

Not sure why you're repeating what I wrote?

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".

You're looking in the wrong place. You're looking at syntax, whereas
what constitutes a constructor call (or just a conversion of e.g.
built-in type) is a semantic issue. Possibly §12.6.1 about "explicit
initialization" is in the direction of what you're looking for.

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 ...).

Well, that's bull. :) And as noted in earlier debates about this,
although terminology is very much a personal choice, in order to
understand the standard, e.g. /definition/ of default constructor using
the word "call", you really have to accept also the meaning of "call"
that you prefer to not ordinarily use, namely, a source code level
expression.

Cheers, & hth.,

- Alf
 
A

Abhishek Padmanabh

On Dec 4, 4:47 am, Abhishek Padmanabh <[email protected]>
wrote:

[...]
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?
 
A

Alf P. Steinbach

* Abhishek Padmanabh:
On Dec 4, 4:47 am, Abhishek Padmanabh <[email protected]>
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>)?

"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.


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?



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.


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.

and if I had a different argument list for this - would that be
allowed?

Subject to the constraints on the signature of an "operator new", yes.

However, best advice is to stay away from low level features (and these
are very low level features) of the language, unless you are experienced
and really really need them and understand the implications of using them.

Cheers, & hth.,

- Alf
 
A

Abhishek Padmanabh

* Abhishek Padmanabh:
On Dec 4, 4:47 am, Abhishek Padmanabh <[email protected]>
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.

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 ::eek:perator 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 ::eek:perator 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
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.
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.


In the current standard this is §18.4.1.3.



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?
 
A

Abhishek Padmanabh

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?
 
J

James Kanze

On Dec 4, 4:47 am, Abhishek Padmanabh <[email protected]>
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.
 
A

Abhishek Padmanabh

First off, thank you very much for the help so far!

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.
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.
If the signature is a direct match, they are replacements, not
overloads.


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.
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.
 
A

Alf P. Steinbach

* Abhishek Padmanabh:
This was unanswered, where else would you invoke the destructor, as in
objectOfClassA.~A(), if not in the context of in-place construction?

I've never had occasion to do so.

One hypothethical case might be where you call a destructor of a global
object and then abort().

And generalizing that hypothetical case, at program termination or even
for ordinary execution you might call destructors of new'ed objects, in
order to achieve the side effects of those destructors without incurring
the overhead of per object dynamic memory deallocation, since under the
specific OS that your program runs, it will all be efficiently
deallocated by the OS anyway.

Those are optimizations that I don't think I'd ever employ.

But since they might be required and justified in some contexts (don't
ask! :)), the original statement was to sweeping.


Cheers, & hth.,

- Alf
 
A

Abhishek Padmanabh

* Abhishek Padmanabh:



I've never had occasion to do so.

One hypothethical case might be where you call a destructor of a global
object and then abort().

And generalizing that hypothetical case, at program termination or even
for ordinary execution you might call destructors of new'ed objects, in
order to achieve the side effects of those destructors without incurring
the overhead of per object dynamic memory deallocation, since under the
specific OS that your program runs, it will all be efficiently
deallocated by the OS anyway.

Those are optimizations that I don't think I'd ever employ.

But since they might be required and justified in some contexts (don't
ask! :)), the original statement was to sweeping.

Okay, I won't ask. :) I saw that the standards is also not so rigid
about it. [class.dtor]/13:

Note: explicit calls of destructors are rarely needed. One use of such
calls is for objects placed at specific addresses using a new-
expression with the placement option. Such use of explicit placement
and destruction of objects can be necessary to cope with dedicated
hardware resources and for writing memory management facilities.

So, yeah, as the example given by you - for side effects (there may be
better ways to achieve it, why not a normal member function to do
whatever cleaning/freeing etc), or any other reason (that I haven't
come across and/or know of yet), one could possibly make an "explicit
destructor call" (destructor is not explicit ;)). Thank you!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,586
Members
45,096
Latest member
ThurmanCre

Latest Threads

Top