Reputation: 4249
Is is legal to access a data member of a class from within a placement-new overload defined in that class?
#include <iostream>
#include <cstdlib>
using namespace std;
class MyClass
{
public:
MyClass ()
{
// Non-default constructor to make sure m_BaseAddress doesn't get
// overwritten.
}
// T is some class that directly or indirectly inherits from MyClass.
template<typename T>
static void* operator new (size_t /* numBytes */, T* memory)
{
auto object = static_cast<MyClass*>(memory);
object->m_BaseAddress = memory;
return static_cast<void*>(memory);
}
template<typename T>
static void operator delete (void* /* memory */, T* /* memory */)
{
}
void* GetBaseAddress ()
{
return m_BaseAddress;
}
private:
void* m_BaseAddress;
};
int wmain ()
{
auto memory = reinterpret_cast<MyClass*>(malloc(sizeof(MyClass)));
auto object = new (memory) MyClass();
wcout << L"Expected: " << memory << endl;
wcout << L"Actual: " << object->GetBaseAddress() << endl;
return 0;
}
I'm using a templatized placement-new in an attempt to make it work even if what is being "newed" is an instance of a class that inherits from MyClass
.
Use-case: I use a special allocator to allocate memory for objects. There are some auxiliary properties associated with the allocated memory that I can query from this allocator if I give it the base address of the allocated memory. To handle the placement-new and storing of this base address, I use a class (MyClass
in the example above) that I use as a base class for all classes that need to be allocated on this special heap. Since the placement-new already gets the base address as a parameter, I was wondering if I can set the member directly, rather than requiring that it be passed as a parameter in the constructor as well.
Upvotes: 4
Views: 116
Reputation: 114795
The code you post has undefined behaviour, a placement new
is used before the constructor is run. This means that you're referring to MyClass::m_BaseAddress
before MyClass
's constructor has run so the static_cast
is a lie and the program is invalid.
The standard explicitly says this is undefined behaviour in section 3.8.5 Object lifetime (emphasis mine).
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type
void*
, is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if:
• the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,
• the pointer is used to access a non-static data member or call a non-static member function of the object
You can see that this is what's happening by adding some prints to your code.
The program will probably work fine with this undefined behaviour but it's still an invalid C++ program and should be avoided.
Please ignore previous edits of this answer :(
Upvotes: 2