将operator new(sizeof(T)*N)返回的内存作为数组处理

xor*_*256 9 c++ arrays placement-new language-lawyer alignas

在C中,可以使用malloc(sizeof(T)*N)分配动态数组,然后使用指针算法在此动态数组中获取i偏移的元素.

在C++中,可以使用operator new()以与malloc()相同的方式执行类似的操作,然后使用new(例如,可以在书中看到第13项的解决方案"Exceptional C++:47工程难题,编程问题和解决方案"通过Herb Sutter).如果您没有,则此问题的解决方案摘要如下:

T* storage = operator new(sizeof(T)*size);

// insert element    
T* p = storage + i;
new (p) T(element);

// get element
T* element = storage[i];
Run Code Online (Sandbox Code Playgroud)

对我来说,这看起来是合法的,因为我要求一大块内存,有足够的内存来容纳n个对齐的size = sizeof(T)元素.因为sizeof(T)应该返回一个对齐的元素的大小,并且它们在一块内存中一个接一个地放置,所以在这里使用指针算法是可以的.

然而,我随后指出了以下链接:http://eel.is/c++draft/expr.add#4http://eel.is/c++draft/intro.object#def:object并声称在C++运算符new()中没有返回一个数组对象,因此对它返回的内容进行指针算术并将其用作数组是未定义的行为,而不是ANSI C.

我不是很擅长这么低级别的东西而且我真的想通过阅读这个来理解:https ://www.ibm.com/developerworks/library/pa-dalign/或者这个:http:// jrruethe. github.io/blog/2015/08/23/placement-new/但我还是不明白,如果萨特是完全错误的?

我确实理解alignas在构造中是有意义的,例如:

alignas(double) char array[sizeof(double)];
Run Code Online (Sandbox Code Playgroud)

(c)http://georgeflanagin.com/alignas.php

如果数组看起来不在double的边界内(可能是在2字节读取处理器的结构中跟随char).

但这是不同的 - 我已经从堆/空闲存储请求内存,特别是请求的操作符new返回内存,该内存将保持与sizeof(T)对齐的元素.

总结一下这是TL; DR:

  • 是否可以在C++中对动态数组使用malloc()?
  • 是否可以在没有alignas关键字的旧C++中为动态数组使用operator new()和placement new?
  • 在运算符new()返回的内存上使用poiner算术未定义的行为?
  • Sutter建议代码哪些可能会破坏某些古董机器?

对不起,如果这是愚蠢的.

Bri*_*ian 8

分配内存上的指针算法问题,如示例所示:

T* storage = operator new(sizeof(T)*size);
// ...
T* p = storage + i;  // precondition: 0 <= i < size
new (p) T(element);
Run Code Online (Sandbox Code Playgroud)

技术上未定义的行为早已为人所知.这意味着std::vector无法使用明确定义的行为纯粹作为库实现,但需要额外的保证,除了标准中的实现.

标准委员会的意图绝对不是无法std::vector实现的.当然,Sutter是正确的,这样的代码旨在明确定义.标准的措辞需要反映出来.

P0593是一项提案,如果被接受进入标准,可能能够解决这个问题.与此同时,继续编写如上所述的代码是可以的; 没有主流编译器会将其视为UB.

编辑:正如在评论中指出,我应该说明的是,当我说 storage + i下,P0593将被明确定义的,我是假设这些元素storage[0],storage[1]......,storage[i-1]在已建成.虽然我不知道我的理解P0593不够好断定它不会也涵盖这些元素的情况下,还没有已建成.

  • @geza它说* any *元素类型的数组类型是隐式生命周期类型(不管元素类型是否是隐式生命周期类型)。因为如果您已经在内存中排列了一堆类型为T的对象,则无需额外的代码即可创建一个T数组。 (2认同)

Kit*_*it. 3

C++ 标准包含一个未解决的问题,即对象的底层表示不是“数组”,而是unsigned char对象的“序列”。尽管如此,每个人都将其视为数组(这是有意的),因此可以安全地编写如下代码:

char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i;  // precondition: 0 <= i < size
new (p) T(element);
Run Code Online (Sandbox Code Playgroud)

只要void* operator new(size_t)返回正确对齐的值即可。使用sizeof-multiplied 偏移量来保持对齐是安全的

在 C++17 中,有一个宏STDCPP_DEFAULT_NEW_ALIGNMENT,它指定 "normal" 的最大安全对齐void* operator new(size_t)void* operator new(std::size_t size, std::align_val_t alignment)如果需要更大的对齐,则应使用该宏。

在早期版本的 C++ 中,没有这样的区别,这意味着void* operator new(size_t)需要以与任何对象的对齐方式兼容的方式来实现。

至于能够直接在 上进行指针算术T*,我不确定标准是否需要这样做然而,很难以一种无法工作的方式来实现 C++ 内存模型。