从类级别放置新的重载中访问成员

时间:2016-02-01 08:28:44

标签: c++

从该类中定义的placement-new重载中访问类的数据成员是否合法?

#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;
}

我正在使用模板化的placement-new尝试使其工作,即使“newed”是继承自MyClass的类的实例。

用例:我使用特殊的分配器为对象分配内存。有一些与分配的内存相关的辅助属性,如果我给它分配的内存的基地址,我可以从这个分配器查询。为了处理这个基地址的放置和存储,我使用了一个类(上例中的MyClass),我用它作为需要在这个特殊堆上分配的所有类的基类。由于placement-new已经将基地址作为参数获取,我想知道我是否可以直接设置成员,而不是要求它也作为参数在构造函数中传递。

1 个答案:

答案 0 :(得分:2)

您发布的代码具有未定义的行为,在构造函数运行之前使用了展示位置new。这意味着您在MyClass::m_BaseAddress的构造函数运行之前引用了MyClass,因此static_cast是谎言且程序无效。

标准明确说明这是 3.8.5对象生存期(强调我的)部分中未定义的行为。

  

在对象的生命周期开始之前,但在对象占用的存储空间已经分配之后,或者在对象的生命周期结束之后以及对象占用的存储空间被重用之前或者,可以使用任何指向对象所在或所在的存储位置的指针,但只能以有限的方式使用。对于正在建造或销毁的物体,见12.7。否则,这样的指针指的是已分配的存储(3.7.4.2),并且使用指针就好像指针属于void*类型一样,是很好的定义。这样的指针可以被解除引用,但是所得到的左值可以仅以有限的方式使用,如下所述。 如果
该程序具有未定义的行为   •对象将是或具有非平凡析构函数的类类型,并且指针用作delete-expression的操作数,
  •指针用于访问非静态数据成员或调用对象的非静态成员函数

您可以通过添加some prints to your code来了解这是正在发生的事情。

该程序可能会在这种未定义的行为中正常工作,但它仍然是一个无效的C ++程序,应该避免使用。

请忽略此答案的先前编辑:(