new and delete

J

junw2000

Hi,
Below is a small code about memory allocation and deallocation.

#include <cstdlib>
#include <iostream>
using namespace std;

class X {
public:
void* operator new(size_t sz) throw (const char*) {
void* p = malloc(sz); //LINE1
if (p == 0) throw "malloc() failed";
return p;
}

void operator delete(void* p) {
free(p);
}

};

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl; //LINE2
free(p);
};

};

int main() {
X* ptr = new X; //LINE3

// call X::eek:perator delete(void*)
delete ptr;

Y* yptr = new Y;

// call Y::eek:perator delete(void*, size_t)
// with size of Y as second argument
delete yptr; //LINE4
}


My questions are:
When LINE3 is executed, LINE1 is executed. How is the variable sz
assigned sizeof(X)?

The output of the code is:
Freeing 400 byte(s)
When LINE4 is executed, LINE2 is executed. How does sz get the value
400?

Thanks a lot.

Jack
 
H

Howard

Hi,
Below is a small code about memory allocation and deallocation.

#include <cstdlib>
#include <iostream>
using namespace std;

class X {
public:
void* operator new(size_t sz) throw (const char*) {
void* p = malloc(sz); //LINE1
if (p == 0) throw "malloc() failed";
return p;
}

void operator delete(void* p) {
free(p);
}

};

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl; //LINE2
free(p);
};

};

int main() {
X* ptr = new X; //LINE3

// call X::eek:perator delete(void*)
delete ptr;

Y* yptr = new Y;

// call Y::eek:perator delete(void*, size_t)
// with size of Y as second argument
delete yptr; //LINE4
}


My questions are:
When LINE3 is executed, LINE1 is executed. How is the variable sz
assigned sizeof(X)?

The output of the code is:
Freeing 400 byte(s)
When LINE4 is executed, LINE2 is executed. How does sz get the value
400?

It's magic! :)

Actually, the C++ standard doesn't specify how or where new and delete keep
this information stored. That's up to the people who wrote the compiler to
decide. Most likely, some code is executed "behind the scenes" which
determines the size of the object to be created, and stores it in a "hidden"
location, for use later when deleting. (I suppose you could try checking
the generated machine code and see exactly what it's doing.)

-Howard
 
V

Victor Bazarov

Below is a small code about memory allocation and deallocation.

#include <cstdlib>
#include <iostream>
using namespace std;

class X {
public:
void* operator new(size_t sz) throw (const char*) {
void* p = malloc(sz); //LINE1
if (p == 0) throw "malloc() failed";
return p;
}

void operator delete(void* p) {
free(p);
}

};

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl; //LINE2
free(p);
};

};

int main() {
X* ptr = new X; //LINE3

// call X::eek:perator delete(void*)
delete ptr;

Y* yptr = new Y;

// call Y::eek:perator delete(void*, size_t)
// with size of Y as second argument
delete yptr; //LINE4
}


My questions are:
When LINE3 is executed, LINE1 is executed. How is the variable sz
assigned sizeof(X)?

Compiler magic.
The output of the code is:
Freeing 400 byte(s)
When LINE4 is executed, LINE2 is executed. How does sz get the value
400?

Compiler magic.

The 'operator new' and 'operator delete' are called by the executing
environment when you use the "new expression". There are intervening
mechanisms that know how much to pass to *your* 'new'.

Put a breakpoint in your 'operator new' and examine the call stack
under the debugger, when the execution is suspended. YOu will see
something between the 'main' and 'operator new', most likely. What
it is, isn't defined in the language. But it will be there.

You might find "Inside the C++ Object Model" by Lippman worth a look.

V
 
M

mlimber

Hi,
Below is a small code about memory allocation and deallocation.

#include <cstdlib>
#include <iostream>
using namespace std;

class X {
public:
void* operator new(size_t sz) throw (const char*) {
void* p = malloc(sz); //LINE1
if (p == 0) throw "malloc() failed";
return p;
}

void operator delete(void* p) {
free(p);
}

};

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl; //LINE2
free(p);
};

};

int main() {
X* ptr = new X; //LINE3

// call X::eek:perator delete(void*)
delete ptr;

Y* yptr = new Y;

// call Y::eek:perator delete(void*, size_t)
// with size of Y as second argument
delete yptr; //LINE4
}


My questions are:
When LINE3 is executed, LINE1 is executed. How is the variable sz
assigned sizeof(X)?

The output of the code is:
Freeing 400 byte(s)
When LINE4 is executed, LINE2 is executed. How does sz get the value
400?

Thanks a lot.

Jack

A footnote to what Howard and Victor said: your class Y doesn't have a
custom new operator defined to match its delete operator. Consequently,
the default global new is used for creating your Y object, but the
object is deleted by your custom delete, which uses free(). This is
bad. See the FAQ:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.3

See also _C++ Coding Standards_ by Sutter and Alexandrescu, Item 45.

Cheers! --M
 
J

junw2000

A footnote to what Howard and Victor said: your class Y doesn't have a
custom new operator defined to match its delete operator. Consequently,
the default global new is used for creating your Y object, but the
object is deleted by your custom delete, which uses free(). This is
bad. See the FAQ:

Thanks. Now I free() by delete as the NEWLINE in the code below. Do you
think it is correct?

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::delete (Y*) p; //NEWLINE
};

};
 
A

Andrey Tarasevich

...
Thanks. Now I free() by delete as the NEWLINE in the code below. Do you
think it is correct?

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::delete (Y*) p; //NEWLINE
};

};

No, this is not correct. In general case delete-expression does several
different things in sequence, one (and only one) of which is calling
your 'operator delete'. Here you inserted another delete-expression into
your 'operator delete's implementation. This will cause the whole
process to start all over again, which in general will lead to
unpredictable consequences. For example, the code will potentially make
an attempt to call the destructor of the object twice - first time from
the external delete-expression and second time from that
delete-expression you inserted into the 'operator delete'.

What you were trying to do in this case can be expressed correctly as
follows

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};

Note, that in this case instead of invoking another delete-expression,
we simply call the global 'operator delete' explicitly. I hope you
understand the difference between the two.
 
J

junw2000

Thanks.
class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};
But it can not pass compiling. Below is the error:

new-delete.cc: In static member function `static void Y::eek:perator
delete(void*,
unsigned int)':
new-delete.cc:31: error: invalid conversion from `size_t' to `void*'
new-delete.cc:31: error: initializing argument 2 of `void operator
delete(void*, void*)'

It seems to me that the compiler parses NEWLINE as Y::eek:perator
delete(void*,
unsigned int).

When I change it into the code below, it works.
::eek:perator delete(p); //NEWLINE

Is the second argument sz necessary? By the way, the global operator
delete does not invoke destructor, right?

Jack
 
A

Andrey Tarasevich

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};
But it can not pass compiling. Below is the error:

new-delete.cc: In static member function `static void Y::eek:perator
delete(void*,
unsigned int)':
new-delete.cc:31: error: invalid conversion from `size_t' to `void*'
new-delete.cc:31: error: initializing argument 2 of `void operator
delete(void*, void*)'

It seems to me that the compiler parses NEWLINE as Y::eek:perator
delete(void*,
unsigned int).

When I change it into the code below, it works.
::eek:perator delete(p); //NEWLINE

Is the second argument sz necessary?

You are right. The standard library does not provide global 'operator
delete' with size argument. That's why it doesn't compile. You can call
the '::eek:perator delete(p)' instead.
By the way, the global operator
delete does not invoke destructor, right?

'operator delete' functions _never_ invoke destructors. These are raw
memory management functions. Delete-expressions call destructors,
'operator delete' fucntion don't. These are two different things. You
seem to be mixing the two.
 
H

Howard

Andrey Tarasevich said:
...
Thanks. Now I free() by delete as the NEWLINE in the code below. Do you
think it is correct?

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::delete (Y*) p; //NEWLINE
};

};

No, this is not correct. In general case delete-expression does several
different things in sequence, one (and only one) of which is calling
your 'operator delete'. Here you inserted another delete-expression into
your 'operator delete's implementation. This will cause the whole
process to start all over again, which in general will lead to
unpredictable consequences. For example, the code will potentially make
an attempt to call the destructor of the object twice - first time from
the external delete-expression and second time from that
delete-expression you inserted into the 'operator delete'.

What you were trying to do in this case can be expressed correctly as
follows

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};

Note, that in this case instead of invoking another delete-expression,
we simply call the global 'operator delete' explicitly. I hope you
understand the difference between the two.

I thought the problem was that he's missing a new operator for the Y class.
Wouldn't the correct solution be to add that, not modify his delete
operator?

-Howard
 
A

Andrey Tarasevich

Howard said:
...
What you were trying to do in this case can be expressed correctly as
follows

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};

Note, that in this case instead of invoking another delete-expression,
we simply call the global 'operator delete' explicitly. I hope you
understand the difference between the two.

I thought the problem was that he's missing a new operator for the Y class.
Wouldn't the correct solution be to add that, not modify his delete
operator?
...

Well, it depends on how you see it. I'd say that the actual problem was
that the memory deallocation function he used in his 'operator delete'
was potentially unrelated to the memory allocation function used by the
standard '::eek:perator new'. Re-delegating the actual deallocation to the
standard '::eek:perator delete' solved the problem.

Whether having a user-defined deallocation function without providing a
user-defined allocation function is a good style is a separate issue.
 
H

Howard

Andrey Tarasevich said:
Howard said:
...
What you were trying to do in this case can be expressed correctly as
follows

class Y {
int filler[100];
public:

// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
//free(p);
::eek:perator delete(p, sz); //NEWLINE
};
};

Note, that in this case instead of invoking another delete-expression,
we simply call the global 'operator delete' explicitly. I hope you
understand the difference between the two.

I thought the problem was that he's missing a new operator for the Y
class.
Wouldn't the correct solution be to add that, not modify his delete
operator?
...

Well, it depends on how you see it. I'd say that the actual problem was
that the memory deallocation function he used in his 'operator delete'
was potentially unrelated to the memory allocation function used by the
standard '::eek:perator new'. Re-delegating the actual deallocation to the
standard '::eek:perator delete' solved the problem.

Whether having a user-defined deallocation function without providing a
user-defined allocation function is a good style is a separate issue.

Ah, ok. I thought maybe the operator new would be *required* in this case.
I keep learning...

Thanks, and regards,
-Howard
 

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,769
Messages
2,569,582
Members
45,060
Latest member
BuyKetozenseACV

Latest Threads

Top