TripShock
TripShock

Reputation: 4249

Accessing member from within class-level placement new overload

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

Answers (1)

Motti
Motti

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

Related Questions