denidare
denidare

Reputation: 474

Returning a different class pointer by overloaded operator new in a class

Suppose i have this sample code:

class A
{
public:
     static void* operator new(size_t sz);

private:
int xA;
float yA;
};

class B : public A
{
private:
int xB;
float yB;
};

void* A::operator new(size_t sz)
{
   void* ptr = (void*)new B();
   return ptr;
}

int main()
{
B* b = (B*) new A();
// Use b ..
delete b;
return 0;
}

Here the constructors will be called in that order (tested in VS2012):

The first two constructors calls are because of the new B() in the overloaded operator new function. But then the A constuctor will be called again on the pointer returned by the function because the overloaded operator new is supposed to return a pointer to free memory (without creating the object), so the constructor is called again.

If i use the pointer b in this example, is this undefined behaviour?

Upvotes: 1

Views: 251

Answers (3)

James Kanze
James Kanze

Reputation: 153929

The code you posted has endless recursion, since you call A::operator new from within A::operator new; class B inherits the operator new from A.

Beyond that, you lie to the compiler, which results in undefined behavior. After new A, you have a pointer to an object whose type is A. You can legally convert its address to a B*, but all you can do with that B* is convert it back to an A*; anything else is undefined behavior.

And it's not clear what you're trying to achieve with the new B in A::operator new. The compiler will consider any memory returned from an operator new as raw memory; in this case, it will construct an A object in it, and from then on out, all you have is an A object. Any attempt to use it as a B object is undefined behavior. (And of course, if you actually need to destruct the B created in A::operator new, you can't because you've overwritten it.

Finally: you don't have to declare operator new as static; it implicily is, and it's idiomatic not to write the static in this case. Similarly, when assigning the results of new B to a void*, the conversion is idiomatic, and it is idiomatic not to make it explicit. (It's also best to avoid the C style casts, since they hide too many errors.)

Upvotes: 1

The contract of operator new is just the memory allocation, the initialization is done later by the new-expression (by calling the constructor) or by program code if you call the operator directly.

What you are trying to do cannot be done, not like that. You could redesign to use a factory member function that would return a pointer to a B object, for example...

Upvotes: 1

Mats Petersson
Mats Petersson

Reputation: 129374

In general , operator new() should not CREATE an object, it should create space for an object. Your code will overwrite a B object with an A object, and then use it as a B object, and yes, that would be "undefined" (probably covered in the docs under "casting an object to a different type that it wasn't originally created as).

This may appear to work in this particular case, but if the constructor of B is more complex (e.g. there are virtual functions in B), it would immediately fail to work correctly.

If you want to allocate memory for an object, you could do:L

void* A::operator new(size_t sz)
{
   void* ptr = (void*)::new unsigned char[sz];
   return ptr;
}

Now you are not calling two different constructors for the same object!

Upvotes: 1

Related Questions