Please notice the pic; Why the object destructed twicely !?!?!
If Do not use the pointer convertion (" ::delete p; "), the complier will apear a warning, but the object only shows a one destructor. Looking like normal running.
Why?!?! Im chaos.
Because void pointer p is converted to pointer type X*
First delete is for xp, next is for p
Thanks Jonne
But how can i close this warning ?!
You allocated a char*
, so you have to delete a char*
, not a void*
.
Thanks FManga
it has correct!
void* operator new(size_t sz){
cout << "reloading new" << endl;
return ::new char[sz];
}
void operator delete(void* p){
cout << "reloading delete" << endl;
::delete [](char*)p;
}
Hi! Jonne,
Could you tell me this detail ?
I don’t know why yet
Ok I am not an expert on C++
but if you cast void pointer p into type X pointer, that temporary instance must be deleted automatically when the function returns. Therefore destructor of class X is called a second time.
Someone who knows more can correct me if I am wrong
I noticed when only cout in operator delete, the void* p destructor still running.
what means first delete for p , next the complier found a global new for xp, so it do ~X() again ???
A C++ question and nobody notified me!? :P
(I’ve left out the std::cout
code to simplify the explanation.)
In the case with the cast, what happens is:
-
new X
is evaluated-
X::new
is called - An array of
char
(char[sz]
) is allocated with::new[]
- A pointer to that array is returned as a
void *
-
- The resulting
void *
is initialised withX::X()
, then converted toX *
and assigned to the variablexp
-
delete xp;
is evaluated- The object at
xp
is deconstructed withX::~X()
-
X::delete
is called -
p
(avoid *
) is converted toX *
- The object at
p
is deconstructed withX::~X()
- The memory at
p
is deallocated by::delete
- The object at
In the case without the cast what happens is:
-
new X
is evaluated-
X::new
is called - An array of
char
(char[sz]
) is allocated with::new[]
- A pointer to that array is returned as a
void *
-
- The resulting
void *
is initialised withX::X()
, then converted toX *
and assigned to the variablexp
-
delete xp;
is evaluated- The object at
xp
is deconstructed withX::~X()
-
X::delete
is called - The memory at
p
(avoid *
) is deallocated by::delete
- The object at
The reason for this is because of how ::new
and ::delete
work.
Functionally new
and delete
are much like C’s malloc
and free
*,
but with the added caveat that the compiler will insert the correct constructor and destructor calls after new
and before delete
(respectively).
In fact new
and delete
are often implemented with std::malloc
and std::free
,
because that’s how they are intended to be implemented.
(Note though they technically don’t have to be implemented that way, but I won’t get into the specifics of that now.)
Technically you allocated char[sz]
with ::new[]
,
so it must be deallocated with ::delete[]
called on char *
.
(Also to convert the void *
to char *
you should prefer static_cast
.)
So the ideal X
should look something like this:
class X
{
public:
X()
{
std::cout << "ctor\n";
}
~X()
{
std::cout << "dtor\n";
}
void * operator new(std::size_t size)
{
std::cout << "new\n";
return ::new char[size];
}
void operator delete(void * pointer)
{
std::cout << "delete\n";
::delete[] static_cast<char *>(pointer);
}
};
While I’m at it, I’d like to mention that using namespace std;
is bad practice,
and that you should use \n
rather than std::endl
;
std::endl
doesn’t just print a new line, it also forces the stream to be flushed,
which is an expensive and often unnecessary operation.
(See https://en.cppreference.com/w/cpp/io/manip/endl for more info.)
Also you should use std::size_t
because technically <cstddef>
is required to declare std::size_t
but there’s no requirement for it to declare ::size_t
.
(See https://en.cppreference.com/w/cpp/header#C_compatibility_headers for more info.)
Almost right, but not quite.
Technically it’s called a second time simply because of the compiler inserting calls to the constructor.
When delete xp
is called, it gets translated to:
xp->~X();
X::delete(xp);
Then when delete (X*)p;
is called, it gets translated to:
(static_cast<X *>(p))->~X();
::delete(static_cast<X *>(p));
Techincally speaking, even when the correct code is used, there’s still a second destructor being called.
In the solution I gave earlier in my comment, ::delete[] static_cast<char *>(pointer);
, the code is translated to:
for(std::size_t index = 0; index < /*number of chars*/; ++index)
(static_cast<char *>(pointer)[index]).~char();
::delete[](static_cast<char *>(pointer));
The exact details of how the destructor is called for each char
object are (I believe) implementation defined, hence I’ve left a comment instead of a proper expression.
But what’s notable about this is that ~char
is technically a nop, so that entire loop will be optimised away by the compiler.
(I think that optimisation might actually be required by the standard, though I’m not completely sure, I’d have to check.)
Lastly, @79859899, why are you creating custom new
and delete
operators in the first place?
If it’s just for learning purposes then fair enough, but if it’s for something specific then you’re probably trying to do the wrong thing.
I’m not saying you won’t ever need to know how to do it,
but it’s not something you should ever need to do in a normal program.
You should only ever need to do it if you’re implementing C++ infrastructure for an environment that doesn’t already have it.
E.g. Arduino didn’t use to have a placement new
operator (and then I came along…).
thanks Pharap!!!
it’s just a exercise in <thinking in C++> new and delete chapter.
Fair enough.
Like I say though, you probably won’t ever need to write a custom new
or delete
,
so it’s probably not a very practical excercise.