从新分配的内存访问对象

时间:2019-07-18 22:26:04

标签: c++ object assignment-operator

如何为数组分配行内存,然后对该数组中的对象调用赋值运算符?

例如:

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.
}

2 个答案:

答案 0 :(得分:2)

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

(当前的C ++ 20草案在[basic.life]中进行了一些更改,这些更改似乎也排除了琐碎的默认构造函数的情况,但我并不完全确定其含义。)

正确,安全的方法是使用“新放置”:

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-expression调用。如果该示例过于简化,并且您确实还有其他原因需要默认初始化该对象(假设可以进行默认初始化),然后重新分配它,那也很简单:

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

答案 1 :(得分:0)

否,它无效。 data[pos]不仅处于不确定的状态。 T指向的内存中根本没有data对象。因此,在不存在的对象上调用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);
}

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