从新分配的内存中分配给对象

Posted

技术标签:

【中文标题】从新分配的内存中分配给对象【英文标题】:assinging to an object from freshly allocated memory 【发布时间】:2019-11-27 21:50:52 【问题描述】:

为一个数组分配行内存然后对该数组中的对象调用赋值运算符是否符合标准?

例如:

template <typename T>
void func(size_t sz, size_t pos)

static constexpr std::align_val_t __al = std::align_val_t(alignof(T));
T* data= static_cast<T*>(::operator new(sz * sizeof(T), __al));
data[pos] = T(1, 2); //is this legal? thechnically object at data[pos] is in some undetermenistic state.

【问题讨论】:

不要使用以下划线开头的标识符。这些是为 C++ 实现保留的。 @aschepler — 这只是保留名称包含两个连续下划线的规则的特例。 @RemyLebeau 它的标识符以 一个 下划线开头,然后是一个大写字母,或者包含两个连续的下划线。 @aschepler 你是对的,我误读了en.cppreference.com/w/cpp/language/identifiers 所说的内容。 【参考方案1】:

这仅对标量类型(如数字类型或任何指针类型)或具有"trivial" default constructor 的类类型(包括联合)或此类类型的数组有效。如果T 是具有非平凡默认构造函数或没有默认构造函数的类类型,或者是此类类类型的数组,则在没有创建对象的内存上调用任何成员函数都是未定义的行为,即使该成员是复制赋值运算符。

(当前的 C++20 草案在 [basic.life] 中有一些变化,这似乎也排除了微不足道的默认构造函数的情况,但我并不完全确定其中的含义。)

正确且安全的方法是使用“placement new”:

template <typename T>
void func(size_t sz, size_t pos)

    static constexpr std::align_val_t al = std::align_val_t(alignof(T));
    std::byte* buffer = static_cast<std::byte*>(::operator new(sz * sizeof(T), al));
    T* data = ::new(static_cast<void*>(buffer + pos*sizeof(T))) T(1, 2);

上面将参数1, 2 直接传递给T 的构造函数,该构造函数由new 表达式调用。如果该示例过于简单化,并且您确实有其他原因要默认初始化对象(假设可以进行默认初始化)然后重新分配它,那也很简单:

template <typename T>
void func(size_t sz, size_t pos)

    static constexpr std::align_val_t __al = std::align_val_t(alignof(T));
    std::byte* buffer = static_cast<std::byte*>(::operator new(sz * sizeof(T), al));
    T* data = ::new(static_cast<void*>(buffer + pos*sizeof(T))) T(1, 2);
    // Whatever other logic...
    data[pos] = T(1, 2);

【讨论】:

【参考方案2】:

不,它是无效的。 data[pos] 不只是处于某种未确定的状态。在data 指向的内存中根本不存在T 对象。因此在不存在的对象上调用T::operator= 是无效的。

在这种情况下,您需要使用布置new

template <typename T>
void func(size_t sz, size_t pos)

    static constexpr std::align_val_t __al = std::align_val_t(alignof(T));
    T* data = static_cast<T*>(::operator new(sz * sizeof(T), __al));
    T* ptr = new (data + pos) T(1, 2);

这会在内存地址data + pos 处构造一个新的T 对象。在释放分配的内存之前,您需要手动调用T 的析构函数来销毁该对象。

【讨论】:

以上是关于从新分配的内存中分配给对象的主要内容,如果未能解决你的问题,请参考以下文章

同一类的一个对象如何在 c++ 中分配给同一类的另一个对象? [关闭]

Java 中的堆和栈

JAVA虚拟机内存分配与回收机制

Java中堆内存与栈内存分配浅析

JVM 内存分配与回收策略

JVM 内存分配与回收策略