放置到缓冲区之后,缓冲区和实例是否具有相同的void*地址?

rav*_*ang 14 c++ placement-new language-lawyer c++11

请参阅以下代码:

unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Run Code Online (Sandbox Code Playgroud)

我可以假设(void*)t == (void*)p吗?

Sto*_*ica 12

是的你可以.我相信这是由几项条款保证的.

  1. [expr.new]/10 - 强调我的

    new-expression将请求的空间量传递给分配函数,作为std :: size_t类型的第一个参数.该参数不得小于正在创建的对象的大小; 仅当对象是数组时,它可能大于正在创建的对象的大小.对于char和unsigned char的数组,new-expression的结果与分配函数返回的地址之间的差异应该是任何对象类型的最严格的基本对齐要求([basic.align])的整数倍.不大于正在创建的数组的大小.[注意:因为假定分配函数返回指向存储的指针,该存储适当地对齐具有基本对齐的任何类型的对象,所以这种对数组分配开销的约束允许分配字符数组的常用习惯用法,稍后将放置其他类型的对象. - 结束说明]

    对我来说,新表达式必须在分配函数返回的确切地址处创建一个对象(假设它不是数组类型).由于您使用的是内置展示位置new,因此我们将介绍以下内容

  2. [new.delete.placement]

    这些函数是保留的,C++程序可能无法定义替换标准C++库中的版本的函数([constraints]).([basic.stc.dynamic])的规定不适用于operator new和operator delete的这些保留的放置形式.

    void* operator new(std::size_t size, void* ptr) noexcept;
    
    Run Code Online (Sandbox Code Playgroud)

    返回:ptr.

    备注:故意不执行任何其他操作.

    这保证了传递给表达式的地址是您分配的字符数组对象的确切地址.那是因为转换void*不会改变源地址.

我认为这足以承诺地址是相同的,即使指针通常不可互换.所以根据[expr.eq]/1(感谢@TC):

相同类型的两个指针比较相等,当且仅当它们都为空时,都指向相同的函数,或者两者都表示相同的地址([basic.compound]).

比较必须产生真,再次因为地址是相同的.

  • 在任何情况下我都不认为你应该删除它,这是一个很好的答案.我也发现了这个:[expr.new] 1*"如果实体是一个非数组对象,new-expression返回一个指向所创建对象的指针.如果它是一个数组,new-expression返回一个指针指向数组的初始元素."*它似乎允许MSVC奇怪的数组处理. (2认同)

Max*_*kin 8

我可以假设(void*)t == (void*)p吗?

不必要.

CLASS::operator new(size_t, unsigned char*)例如,如果类的作者重载,那么该运算符可以返回除第二个参数之外的任何内容,例如:

struct CLASS
{
    static void* operator new(size_t, unsigned char* p) { return p + 1; }
};
Run Code Online (Sandbox Code Playgroud)

如果您希望这个新表达式调用代码所需的标准非分配放置新运算符

  1. 包括标题<new>.
  2. 确保传递一个void*参数.
  3. 如果有的话,使用范围解析运算符对其::进行前缀以绕过CLASS::operator new.

例如:

#include <new> 
#include <cassert> 

unsigned char p[sizeof(CLASS)];
CLASS* t = ::new (static_cast<void*>(p)) CLASS;
assert(t == static_cast<void*>(p));
Run Code Online (Sandbox Code Playgroud)

在这种情况下t == static_cast<void*>(p)确实.

事实上,这就是GNU C++标准库的作用:

template<typename _T1, typename... _Args>
inline void _Construct(_T1* __p, _Args&&... __args) { 
    ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); 
}
Run Code Online (Sandbox Code Playgroud)