Sign in to follow this  
Skarsha

overloading new, then using placement new.

Recommended Posts

Skarsha    132
I'm trying to call a placement new on an object that has it's new overloaded. It seems that somehow the "new" function used in the placement new must be different then the normal new, right? Because as soon as I define a new default operator new, I get errors, even if the new is the same syntax as the global default new.
#include <new>

// Dummy class, ignore.
class Bar {
	int m_y;
};

class Foo {
  public:
	Foo() {}
	~Foo() {}

	//void* operator new(size_t size, void* place) { return place; } // <----- THIS FIXES THE BUG. DO I HAVE TO DO THIS?
	//void* operator new(size_t size, Bar *bar) { return malloc(size); }
	void* operator new(size_t size) { return malloc(size); }   // <------- THIS IS THE CAUSE OF THE BUG.
	void operator delete(void* data, Bar *bar) { free(data); }

  private:
	int m_x;
};

int main() {
	void* raw = malloc(sizeof(Foo));
	// Allocate the raw memory to be used in the placement new.

	Foo* p = new(raw) Foo();
	// Attempt the placement new. <----- THIS IS WHERE THE BUG SHOWS.

	p->~Foo();
	// Placement delete.

	free(raw);
	// Remove the memory.

	return 1;
}

Quote:
Build Output (MSVC++ 2005 Express Edition.) error C2660: 'Foo::operator new' : function does not take 2 arguments
Do I really need to define
  void* operator new(size_t size, void* place)
as a function that will not be called when placement new is used? Man, why can't we just call Foo->Foo()... Thanks for all assistance.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:

Do I really need to define

void* operator new(size_t size, void* place)

as a function that will not be called when placement new is used?

What do you mean? It *will* be called if you use placement new.
Quote:
Man, why can't we just call Foo->Foo()...
If you want to reinitialize an object, why not just make an init-function and have the constructor call that? You can then call this init again when you want.

Share this post


Link to post
Share on other sites
Enigma    1410
Declaring any form of operator new or operator delete in a class hides all other forms of operator new or operator delete that are not redeclared in that class. For this reason any time you provide an overload of at least one form of operator new or operator delete you should provide overloads for all forms of operator new and operator delete.

Also note that your reimplementation of the normal form of operator new does not behave the same as the default operator new. In particular default operator new will never return a null pointer, whereas your overload of operator new might. mallocs behaviour may also not be acceptable in the case of a zero sized allocation.

Finally, what would you expect the syntax Foo->Foo() to do? Stack allocation? We already have a syntax for that. Reinitialisation? We already have a syntax for that (foo = Foo();). Something else?

Σnigma

Share this post


Link to post
Share on other sites
Skarsha    132
Hmm, why does placement new call my overloaded Foo new if it exists, but doesn't otherwise call the overloaded global new.



#include <new>
#include <stdio.h>

void* ::operator new(size_t size) {
printf("\nglobal new");
return malloc(size);
}

// Dummy class, ignore.
class Bar {
int m_y;
};

class Foo {
public:
Foo() { printf("\nctor"); }
~Foo() { printf("\ndtor"); }

//void* operator new(size_t size, void* place) { printf("\nnew called"); return place; } // <----- (#1) I DON'T ACTUALLY WANT THIS TO BE CALLED.
//void* operator new(size_t size, Bar *bar) { return malloc(size); }
//void* operator new(size_t size) { return malloc(size); }
void operator delete(void* data, Bar *bar) { free(data); }

private:
int m_x;
};

int main() {
freopen("stdout.txt", "wb", stdout);
printf("test");

void* raw = malloc(sizeof(Foo));
// Allocate the raw memory to be used in the placement new.

Foo* p = new(raw) Foo();
// Attempt the placement new. <----- THIS IS WHERE THE BUG IS.

p->~Foo();
// Placement delete.

free(raw);
// Remove the memory.

return 1;
}


If I uncomment #1, it calls my overloaded new.

Outputs:

#1 uncommented. Unfortunately, "new" is called.

test
new called
ctor
dtor



#1 commented. No "new" called.

test
ctor
dtor





Maybe I should rephrase the question:

How can I call a constructor (without actually calling an "operator new" function) after I have overloaded new.



Quote:
From AP
If you want to reinitialize an object, why not just make an init-function and have the constructor call that? You can then call this init again when you want.

It's just me playing with allocation syntax, so I'd have to write an init-function for every class.

Share this post


Link to post
Share on other sites
Skarsha    132
Quote:
Original post by Enigma
Finally, what would you expect the syntax Foo->Foo() to do? Stack allocation? We already have a syntax for that. Reinitialisation? We already have a syntax for that (foo = Foo();). Something else?
Σnigma



Um, I suppose it could be called "initialisation" (As opposed to "_re_initialisation).
Basically, it would call function #1 (in the code). You could, of course, pass extra constructor parameters for non-default constructors.

Code:

class Foo() {
Foo() { printf("\nctor"); } // <---- (#1)
~Foo() { printf("\ndtor"); }
};

int main() {
Foo* foo = (Foo*)malloc(sizeof(Foo));
printf("\n<0>");

foo->Foo();
printf("\n<1>");

foo->~Foo();
printf("\n<2>");

free(foo);
printf("\n<3>");
}


NOTE: Won't compile, this is what I WISH it did.

Output:

<0>
ctor
<1>
dtor
<2>
<3>

Share this post


Link to post
Share on other sites
Enigma    1410
Ah, I see. In that case, what would you expect this to do?
class Foo
{

public:

Foo()
{
std::cout &lt;&lt; "Foo::Foo\n";
}
~Foo()
{
std::cout &lt;&lt; "Foo::~Foo\n";
}
void Bar()
{
std::cout &lt;&lt; "Foo::Bar\n";
}

};

class Bar
:
public Foo
{

public:

Bar()
{
std::cout &lt;&lt; "Bar::Bar\n";
}
~Bar()
{
std::cout &lt;&lt; "Bar::~Bar\n";
}

};

int main()
{
Foo * foo = static_cast&lt; Foo * &gt;(malloc(sizeof(Foo)));
std::cout &lt;&lt; "&lt;0&gt;\n";
foo-&gt;Bar();
std::cout &lt;&lt; "&lt;1&gt;\n";
foo-&gt;~Bar();
std::cout &lt;&lt; "&lt;2&gt;\n";
free(foo);
std::cout &lt;&lt; "&lt;3&gt;\n";
}


As to your earlier question about why your global operator new doesn't get called - It's because you never call global operator new. You replaced global operator new but call placement new (which can't be replaced anyway).

Σnigma

Share this post


Link to post
Share on other sites
Nitage    1107
Overloads of operator new only have to provide memory, they don't have to run constructors. The new operator (different from operator new) is responsible for initialisation - and it can't be overloaded:


T* tPtr = new T;
//The above line performs the following pseudo code
tPtr = operator new(sizeof(T));//either the global or overloaded member version
run constructor on memory pointed to by tPtr;//you can't control this bit

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
All new calls causes the constructor to be called. Use the placement new to call your constructor, thats why its there.


#include // defines placement new

class Foo() {
Foo() { printf("\nctor"); } // ");

// foo->Foo(); not the whay to go
new (foo) Foo;
printf("\n");

foo->~Foo();
printf("\n");

free(foo);
printf("\n");
}


If you want to be able to use both placement new and custom new, you need to define both.


class Foo() {
Foo() { printf("\nctor"); } // <

Share this post


Link to post
Share on other sites
Skarsha    132
Quote:
Original post by Enigma
Ah, I see. In that case, what would you expect this to do?


Fail compilation when you call foo->~Bar() since the class Foo has no member called ~Bar().

But I guess you're trying to say what happens when a class's name is the same as a function of the parent. Shouldn't this just do the default thing of calling the function of the youngest child, since it isn't virtual, which happens to be (in that case) the constructor for Bar.

Maybe if void Foo::Bar() was declared virtual it might be confusing.

So, how about making "ctor" and "dtor" keywords then (still wishing). So you could define a class like:


class Foo() {
public:
ctor() {}
ctor(int x) {}
dtor() {}
dtor(int y) {} // <--- could allow parameters to destructors if this would be a good thing.
}

int main() {
Foo* foo = (Foo*)malloc(sizeof(Foo));

foo->ctor(1); // <--- placement new.
foo->dtor(); // <--- placement delete.

free(foo);

return 0;
}





BTW, your <'s and >'s show as &.l.t.; and &.g.t.; (minus the periods) here. Is that just firefox or has the gd.net tokenizer hiccupped?

Share this post


Link to post
Share on other sites
Skarsha    132
Quote:
From the AP
All new calls causes the constructor to be called. Use the placement new to call your constructor, thats why its there.

I'm trying to use placement new, compiler won't let me once I overload Foo::operator new().


Nitage:
Do you know how I can explicitly call that "thing" that is responsible for initialisation?


Oh yeah, btw that init-function idea has another problem, classes won't call their parent's constructors unless they also have an init-function and the class calls super->init();


Thanks.



Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:
Original post by Skarsha
I'm trying to use placement new, compiler won't let me once I overload Foo::operator new().
It will if you also overload the placement new operator. You already have a solution in your first message with the comment "THIS FIXES THE BUG". Why won't you just use it?

Share this post


Link to post
Share on other sites
Skarsha    132
Quote:
From the AP
It will if you also overload the placement new operator. You already have a solution in your first message with the comment "THIS FIXES THE BUG". Why won't you just use it?

Well, originally because it was inelegant and needed every class to implement a placement overload, but now it's because I don't want to call a function. Although it seems I was already calling a function when calling the original placement new.

I just found the solution tho: call the global placement new, ie:
Foo* p = ::new(raw) Foo();



Thanks all for the help.

Share this post


Link to post
Share on other sites
Nitage    1107
Quote:

Nitage:
Do you know how I can explicitly call that "thing" that is responsible for initialisation?


You can't and you don't ever need to.


This compiles and runs perfectly correctly:

#include <iostream>
#include <memory>

class foo
{
public:
foo()
{
std::cout << "foo()" << std::endl;
}

~foo()
{
std::cout << "~foo()" << std::endl;
}

void* operator new(size_t, void* place) { return place; }

void* operator new(size_t size) { return malloc(size); }
void operator delete(void* mem) { free(mem); }

};

int main()
{
foo* pFoo = (foo*)malloc(sizeof(foo));

new(pFoo) foo;

pFoo->~foo();

free(pFoo);
}



Output is:
foo()
~foo()
as expected.


I don't understand what problem you're having - seems to me that you already have a solution, and Enigma told you why:

Quote:
Original post by Enigma
Declaring any form of operator new or operator delete in a class hides all other forms of operator new or operator delete that are not redeclared in that class. For this reason any time you provide an overload of at least one form of operator new or operator delete you should provide overloads for all forms of operator new and operator delete.

Share this post


Link to post
Share on other sites
Skarsha    132
Nitage:
Yeah, but it looks much nicer now without all the extra new overloads.


Quote:
Original post by Nitage
You can't and you don't ever need to.

But I WANT to :/-


Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:
Original post by Skarsha
Nitage:
Yeah, but it looks much nicer now without all the extra new overloads.


Quote:
Original post by Nitage
You can't and you don't ever need to.

But I WANT to :/-

Shut the fuck up - stupid moron.

Share this post


Link to post
Share on other sites
Skarsha    132
To the AP:
This is kinda the plainest example of where calling that initialisation function would be useful. Need is relative, considering there is nothing that can't be done by typing hex machine code. The lack also makes C++ less consistant.

So stop trolling anonymously and unusefully.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
It's still called consistent.

And of course there is a keyword to call the initialisation function (also called constructor): placement new.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Quote:
Original post by Skarsha
Nitage:
Yeah, but it looks much nicer now without all the extra new overloads.
You can put all the overloads in a class that has only the overloads, and publicly inherit them from it. Then you can reuse this superclass everywhere you need. Isn't that good enough?

Share this post


Link to post
Share on other sites
Enigma    1410
As the AP says (plus demonstrating hopefully correct implementations and all implementations and the circumstances under which each version of delete is called):
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <iostream>

template < typename Type >
struct SpecialAllocator
{

static void * operator new(std::size_t size)
{
std::printf("static void * operator new(std::size_t size)\n");
size = std::max< std::size_t >(size, 1);
void * memory = std::malloc(size);
while (!memory)
{
std::new_handler new_handler = std::set_new_handler(0);
std::set_new_handler(new_handler);
if (!new_handler)
{
throw std::bad_alloc();
}
new_handler();
memory = std::malloc(size);
}
return memory;
}
static void * operator new[](std::size_t size)
{
std::printf("static void * operator new[](std::size_t size)\n");
return operator new(size);
}
static void * operator new(std::size_t size, std::nothrow_t const &)
{
std::printf("static void * operator new(std::size_t size, std::nothrow_t const &)\n");
size = std::max< std::size_t >(size, 1);
return std::malloc(size);
}
static void * operator new[](std::size_t size, std::nothrow_t const &)
{
std::printf("static void * operator new[](std::size_t size, std::nothrow_t const &)\n");
return operator new(size, std::nothrow);
}
static void * operator new(std::size_t size, void * memory)
{
std::printf("static void * operator new(std::size_t size, void * memory)\n");
return memory;
}
static void * operator new[](std::size_t size, void * memory)
{
std::printf("static void * operator new[](std::size_t size, void * memory)\n");
return memory;
}
static void operator delete(void * memory)
{
std::printf("static void operator delete(void * memory)\n");
std::free(memory);
}
static void operator delete[](void * memory)
{
std::printf("static void operator delete[](void * memory)\n");
operator delete(memory);
}
static void operator delete(void * memory, std::nothrow_t const &)
{
std::printf("static void operator delete(void * memory, std::nothrow_t const &)\n");
std::free(memory);
}
static void operator delete[](void * memory, std::nothrow_t const &)
{
std::printf("static void operator delete[](void * memory, std::nothrow_t const &)\n");
operator delete(memory, std::nothrow);
}
static void operator delete(void *, void *)
{
std::printf("static void operator delete(void *, void *)\n");
}
static void operator delete[](void *, void *)
{
std::printf("static void operator delete[](void *, void *)\n");
}

};

class Foo
:
public SpecialAllocator< Foo >
{

public:

Foo()
{
std::cout << "Foo::Foo\n";
}
~Foo()
{
std::cout << "Foo::~Foo\n";
}

};

class Bar
:
public SpecialAllocator< Bar >
{

public:

Bar()
{
std::cout << "Bar::Bar\n";
throw 7;
}
~Bar()
{
std::cout << "Bar::~Bar\n";
}

};

int main()
{
std::iostream::sync_with_stdio(true);
try
{
Foo * foo = static_cast< Foo * >(std::malloc(sizeof(Foo)));
new (foo)Foo();
foo->~Foo();
std::free(foo);
}
catch (...)
{
}
std::cout << "--\n";
try
{
Foo * foo = new Foo();
delete foo;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Foo * foo = new Foo[1];
delete[] foo;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Foo * foo = new (std::nothrow) Foo();
delete foo;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Foo * foo = new (std::nothrow) Foo[1];
delete[] foo;
}
catch (...)
{
}
std::cout << "--\n";
Bar * bar;
try
{
bar = static_cast< Bar * >(std::malloc(sizeof(Bar)));
new (bar)Bar();
bar->~Bar();
std::free(bar);
}
catch (...)
{
std::free(bar);
}
std::cout << "--\n";
try
{
Bar * bar = new Bar();
delete bar;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Bar * bar = new Bar[1];
delete[] bar;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Bar * bar = new (std::nothrow) Bar();
delete bar;
}
catch (...)
{
}
std::cout << "--\n";
try
{
Bar * bar = new (std::nothrow) Bar[1];
delete[] bar;
}
catch (...)
{
}
std::cout << "--\n";
}

Disclaimer: code was quickly hacked together and may contain errors, plus SpecialAllocator really isn't a great name for the class.

Σnigma

Share this post


Link to post
Share on other sites

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