Moo*_*uck 62 c++ standards memory-management placement-new
[expr.new]C++的5.3.4 2月草案给出了一个例子:
new(2,f) T[5]导致打电话operator new[](sizeof(T)*5+y,2,f).这里,x和y是非负的未指定值,表示数组分配开销; new-expression的结果将从返回的值中抵消此数量
operator new[].这种开销可以应用于所有数组新表达式,包括那些引用库函数operator new[](std::size_t, void*)和其他放置分配函数的表达式.开销的数量可能因新的一次调用而异.- 末端的例子 ]
现在来看以下示例代码:
void* buffer = malloc(sizeof(std::string) * 10);
std::string* p = ::new (buffer) std::string[10];
Run Code Online (Sandbox Code Playgroud)
根据上面的引用,第二行将new (buffer) std::string[10]在内部调用operator new[](sizeof(std::string) * 10 + y, buffer)(在构造单个std::string对象之前).问题是如果y > 0,预分配的缓冲区太小了!
那么我如何知道在使用数组放置时预先分配多少内存?
void* buffer = malloc(sizeof(std::string) * 10 + how_much_additional_space);
std::string* p = ::new (buffer) std::string[10];
Run Code Online (Sandbox Code Playgroud)
或者标准某处是否保证y == 0在这种情况下?报价再次说:
这种开销可以应用于所有数组新表达式,包括那些引用库函数
operator new[](std::size_t, void*)和其他放置分配函数的表达式.
How*_*ant 43
operator new[](std::size_t, void* p)除非你事先知道这个问题的答案,否则不要使用.答案是实现细节,可以随编译器/平台而改变.虽然它对任何给定的平台通常都是稳定的.例如,这是Itanium ABI指定的内容.
如果您不知道此问题的答案,请编写您自己的可在运行时检查此问题的展示位置数组:
inline
void*
operator new[](std::size_t n, void* p, std::size_t limit)
{
if (n <= limit)
std::cout << "life is good\n";
else
throw std::bad_alloc();
return p;
}
int main()
{
alignas(std::string) char buffer[100];
std::string* p = new(buffer, sizeof(buffer)) std::string[3];
}
Run Code Online (Sandbox Code Playgroud)
通过改变数组大小并n在上面的示例中进行检查,您可以推断出y您的平台.对于我的平台 y是1个字.sizeof(word)取决于我是在编译32位还是64位架构.
更新:经过一番讨论,我明白我的回答不再适用于这个问题.我会留在这里,但仍然需要一个真正的答案.
如果很快找不到好的答案,我会很乐意以一些赏金来支持这个问题.
据我所知,我会在这里重申这个问题,希望更短的版本可以帮助其他人理解被问到的内容.问题是:
以下结构总是正确的吗?是arr == addr在结束了吗?
void * addr = std::malloc(N * sizeof(T));
T * arr = ::new (addr) T[N]; // #1
Run Code Online (Sandbox Code Playgroud)
我们从标准中知道#1导致调用::operator new[](???, addr),其中???一个未指定的数字不小于N * sizeof(T),并且我们也知道该调用仅返回addr并且没有其他影响.我们也知道相应地arr抵消了addr.我们不知道的是,指向的内存是否addr足够大,或者我们如何知道要分配多少内存.
你似乎混淆了一些事情:
您的示例调用operator new[](),而不是.operator new()
分配函数不构造任何东西.他们分配.
会发生什么是表达式 T * p = new T[10];导致:
调用operator new[]()size参数10 * sizeof(T) + x,
十T,有效地调用默认构造函数::new (p + i) T().
唯一的特点是array-new 表达式要求的内存多于数组数据本身使用的内存.您没有看到任何此类信息,除了默认接受之外,不能以任何方式使用此信息.
如果你很好奇实际分配了多少内存,你可以简单地替换数组分配函数operator new[],operator delete[]并打印出实际大小.
更新:作为随机信息,您应该注意全局布局新功能必须是无操作.也就是说,当你像这样构造一个对象或数组时:
T * p = ::new (buf1) T;
T * arr = ::new (buf10) T[10];
Run Code Online (Sandbox Code Playgroud)
然后相应的调用::operator new(std::size_t, void*)和::operator new[](std::size_t, void*)什么都不做,但返回他们的第二个参数.但是,你不知道buf10应该指出什么:它需要指向10 * sizeof(T) + y内存的字节,但你无法知道y.
operator new[] ()使用固定大小的内存区域调用任何版本都不会很好.本质上,假设它委托一些真正的内存分配函数,而不是只返回指向已分配内存的指针.如果您已经有一个要构建对象数组的内存区域,则需要使用std::uninitialized_fill()或std::uninitialized_copy()构造对象(或单独构造对象的其他形式).
您可能会认为这意味着您必须手动销毁记忆体中的对象.但是,调用delete[] array从放置位置返回的指针new将不起作用:它将使用非放置版本operator delete[] ()!也就是说,使用放置时,new您需要手动销毁对象并释放内存.
正如Kerrek SB在评论中提到的那样,这个缺陷在2004年首次报道,并在2012年得到解决:
CWG同意EWG是处理此问题的适当场所.
然后在2013年向EWG报告了这个缺陷,但是作为NAD关闭(可能意味着"不是缺陷")评论:
问题在于尝试使用array new将数组放入预先存在的存储中.我们不需要使用new new; 只是构建它们.
这大概是指所建议的解决办法是使用一个循环带,以非阵列放置为正在构建的每个对象的新一次呼叫.
线程上其他地方没有提到的推论是,这段代码导致所有人的未定义行为T:
T *ptr = new T[N];
::operator delete[](ptr);
Run Code Online (Sandbox Code Playgroud)
即使我们遵守生命周期规则(即T要么具有微不足道的破坏,或者程序不依赖于析构函数的副作用),问题是ptr已针对此未指定的cookie进行了调整,因此传递给错误的值是错误的.operator delete[].
| 归档时间: |
|
| 查看次数: |
5787 次 |
| 最近记录: |