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

Accessing member from within class-level placement new overload

本文关键字:成员 访问 重载      更新时间:2023-10-16

从类中定义的放置新重载中访问类的数据成员是否合法?

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

我正在使用模板化放置-new,试图使其工作,即使"newed"是从MyClass继承的类的实例。

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

您发布的代码具有未定义的行为,在运行构造函数之前使用放置new。这意味着你在MyClass的构造函数运行之前引用MyClass::m_BaseAddress,所以static_cast是谎言,程序是无效的。

该标准明确指出这是第 3.8.5 节对象生存期中未定义的行为(强调我的(。

在对象的生存期开始之前,但在分配了对象将占用的存储

之后,或者在对象的生存期结束之后,在重新使用或释放对象占用的存储之前,可以使用任何指向对象将要或曾经所在的存储位置的指针,但只能以有限的方式使用。对于正在建造或销毁的物体,请参见 12.7。否则,此类指针引用分配的存储 (3.7.4.2(,并且像指针的类型为 void* 一样使用该指针是明确定义的。可以取消引用此类指针,但生成的左值只能以有限的方式使用,如下所述。在以下情况下,程序具有未定义的行为
• 对象将是或曾经是具有非平凡析构函数的类类型,并且指针用作删除表达式的操作数,
指针用于访问对象的非静态数据成员或调用对象的非静态成员函数

您可以通过向代码添加一些打印件来看到这种情况。

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

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