Sign in to follow this  

placement New/delete

This topic is 3105 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

class A
{
public:
	A(){ v = 6;}
	~A(){}

	//Placement New
	//===============================================
	static void* operator new(size_t s, void* w){
		return w;
	} 

	//New/Delete
	//===============================================
	static void* operator new(size_t s){
		return ::operator new(s);
	} 
	static void operator delete(void* p){
		delete p;
	}

	//Other New/Delete
	//===============================================
	static void* operator new(size_t s, int i){
		return ::operator new(s);
	} 
	static void operator delete(void* p, int i){
		delete p;
	} 

	int v;
};

int main()
{
	A* p = (A*)malloc(sizeof(A));	//allocate space
	new (p) A;						//placement new
	delete p;						//delete
	
	p = new A;	//call new
	delete p;	//call delete

	p = new(6) A;	//call other new
	delete(6) p;	//call other delete

}


1. How does the placement opertator new call, know to call the constructor? I mean, i just returns the pointer that was passed in. I notice that if i return 0 instead of 'w', the constructor *doesnt* get called. How exactly is this working? 2. Why doesnt the last line "delete(6);" compile? 3. How should i implement and call placement delete? Cheers

Share this post


Link to post
Share on other sites
operator new is not the same as the new keyword. Poor choice of operator naming on the standards body's part, but understandable due to their want to not introduce extraneous keywords.

Share this post


Link to post
Share on other sites
Placement delete is called in only one situation: if the constructor of an object throws an exception when allocated with placement new, the corresponding version of placement delete is called. If you want to use a special deallocation method with your object pointer, you'll need to call the destructor manually and then call your deallocation function manually.

Share this post


Link to post
Share on other sites
No, placement delete doesn't call the destructor. Placement delete is called in exactly one situation: when the constructor for an object fails by throwing an exception. If the constructor fails, the destructor is not called, because it makes no sense to call the destructor for something that never finished constructing. You cannot call placement delete manually on an object pointer, so no syntax you can think of that would result in placement delete being called will compile.

Share this post


Link to post
Share on other sites
Quote:
Original post by maya18222
1. How does the placement opertator new call, know to call the constructor?


The operator new doesn't. The expression using the 'new' keyword calls the operator new (in this case, the placement operator new), and then calls the constructor. It knows what constructor to call because it knows the type of the thing being new'd.

Quote:
I mean, i just returns the pointer that was passed in. I notice that if i return 0 instead of 'w', the constructor *doesnt* get called. How exactly is this working?


It isn't actually working. The purpose of the operator new overload is to tell the new keyword where the allocated memory is. Ordinarily, placement new does this by just blindly reporting whatever location it was given, since there is no allocation to do. If you return 0 instead, then the constructor gets called through a null pointer, which is undefined behaviour.

Quote:

2. Why doesnt the last line "delete(6);" compile?

3. How should i implement and call placement delete?


Ordinary 'delete' calls a destructor and deallocates memory. Ordinary 'new' allocates memory and calls a constructor. There is no syntax in the language for explicitly calling a constructor upon an existing object (it isn't a member function), but there is for explictly calling a destructor upon an existing object. And when something has been placement new'd, the way to undo the constructor call is with an explicit destructor call.

It is ***not*** OK to use malloc() to declare memory, initialize it with placement new, and then call delete to "call the destructor and deallocate the memory". 'delete' only defined-ly works with memory that was allocated with (an actual allocating form of) new. You must always deallocate memory in a way that corresponds to how it was allocated.

Also, using an 'int' as a "marker" for the "other" new is a little sketchy.

Your example should look more like (if I've actually understood things as well as I think I have):


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

class marker {} other;

class A
{
public:
A() : v(6) {
// Just for testing!
bool do_throw;
std::cout << "Throw an exception?" << std::endl;
std::cin >> do_throw;
if (do_throw) { throw other; } // evil :)
}

static void operator delete(void* p){
::operator delete(p);
}

// Only gets called if the corresponding 'new' throws an exception.
static void operator delete(void* p, void* w){
// No memory was allocated, so do nothing.
}

// Only gets called if the corresponding 'new' throws an exception.
static void* operator delete(void* p, const marker& i){
std::cerr << "Constructor threw after 'other' new used, so 'other' delete got called" << std::endl;
::operator delete(p);
}


static void* operator new(size_t s, void* w){
return w;
}

static void* operator new(size_t s){
return ::operator new(s);
}

static void* operator new(size_t s, const marker& i) {
std::cerr << "zomg, 'other' new used!" << std::endl;
return ::operator new(s);
}

int v;
};

int main()
{
// Placement new:
try {
A* p = malloc(sizeof(A)); // malloc() doesn't need a cast, and
// you should avoid C-style casts in C++ anyway.
new (p) A;
p::~A(); // explicitly call the destructor
free(p); // deallocate the memory in a way that matches its allocation.
} catch (const marker& exception) {}

try {
// Ordinary new:
p = new A;
delete p;
} catch (const marker& exception) {}

try {
// 'other' new:
p = new(other) A;
delete p; // This is OK: the memory was allocated with ::operator new.
} catch (const marker& exception) {}
}



(Edited to cover exceptions and what the others were saying about other forms of delete being called in those situations.

Share this post


Link to post
Share on other sites
Thanks Zhalman, very helpful, thanks for taking the time :)

But i noticed that i had to correct a number of compilation errors in your code example? Summerised below. Are these just typos?

1. deletes returning void* instead of void
2. p = (A*)malloc(sizeof(A)); ----- Not having the cast wouldnt compile for me
3. Local p defined in try scope
4. p->~A(); rather than p::~A();

Here is my version of your code

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

class marker {} other;

class A
{
public:
A() : v(6) {
//throw other; // evil :)
}


static void* operator new(size_t s, void* w){
return w;
}
// Only gets called if the corresponding 'new' throws an exception.
static void operator delete(void* p, void* w){
// No memory was allocated, so do nothing.
}


static void* operator new(size_t s){
return ::operator new(s);
}
static void operator delete(void* p){
::operator delete(p);
}



static void* operator new(size_t s, const marker& i) {
std::cerr << "zomg, 'other' new used!" << std::endl;
return ::operator new(s);
}
// Only gets called if the corresponding 'new' throws an exception.
static void operator delete(void* p, const marker& i){
std::cerr << "Constructor threw after 'other' new used, so 'other' delete got called" << std::endl;
::operator delete(p);
}

int v;
};

int main()
{
A* p = 0;
// Placement new:
try {
p = (A*)malloc(sizeof(A)); // malloc() doesn't need a cast, and
// you should avoid C-style casts in C++ anyway.
new (p) A;
p->~A(); // explicitly call the destructor
free(p); // deallocate the memory in a way that matches its allocation.
}
catch (const marker& exception){
std::cout << "Cought";
}

try {
// Ordinary new:
p = new A;
delete p;
} catch (const marker& exception) {}

try {
// 'other' new:
p = new(other) A;
delete p; // This is OK: the memory was allocated with ::operator new.
} catch (const marker& exception) {}
}



Also, i have a few more questions if i may.

Whats the purpose of calling the delete operator if a constructor fails? I take it, its too perform some clean up. But that requires you to know what memory was allocated in the constructor doesnt it? Say you had:-


Constructor(){
p = new int();
n = new int();
m = new int();
}



And the delete operator gets called becuase the constructor failed at some point, how would you know which pointer to free/delete? Just assign Null before alloaction, and then check for non null to delete?

I take it that a better method for the above problem would be to just use some form of auto pointer. As i believe, that if a constructor fails, the objects that were constructed inside of the object that failed construction, get their destructors called.

Share this post


Link to post
Share on other sites
Quote:

Whats the purpose of calling the delete operator if a constructor fails?

Consider:

#include <iostream>

class Example
{
public:
Example() { throw 42; }

void *operator new(size_t amount) {
std::cout << "operator new\n";
return ::operator new(amount);
}

void operator delete(void *ptr) {
std::cout << "operator delete\n";
return ::operator delete(ptr);
}
};

int main()
{
for(int i = 0 ; i < 3 ; ++i )
{
try
{
Example *example = new Example();
}
catch(int)
{
}
}
}


Consider what happens if operator delete did not automatically free the memory that was allocated by operator new, in the event the constructor throws an exception. There is no way to recover the pointer to free it later, we would have a memory leak.

In your example (changing "int" to "Foo":

Constructor(){
p = new Foo();
n = new Foo();
m = new Foo();
}


If one of these throws (say the last one), then *it* will be cleaned up. The others will not, they leak. This is why we use smart pointers.

Share this post


Link to post
Share on other sites
Ah, sorry. Got a little confused there, i thought operator delete was also there to delete memory allocated by the *members* in the constructor. But now i understand its just for the space the Class object *itself* is taking up, not anything alloacted by members.

so considering


class A
{
public:
A(){
p = new int();
}
~A(){
delete p;
}

void* operator new(size_t s)
{
return ::operator new(s);
}

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

int* p;
int p2;
};



You would never delete p in the "operator delete" as it woould be deleted twice, in the destructor first, and then again in the "operator delete". So the "operator delete" would just be deleting data such as p2.

Share this post


Link to post
Share on other sites

This topic is 3105 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this