请参阅下面的代码:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
我可以假设(void*)t == (void*)p
吗?
是的,你可以。我相信这是有几项规定的保证。
-
[exr.new]/10 - 强调我的
新表达式将请求的空间量传递给 分配函数作为类型std::size_t的第一个参数。 参数应不小于正在创建的对象的大小; 它可能大于正在创建的对象的大小,仅当 对象是一个数组。对于字符和无符号字符数组, 新表达式的结果与地址之间的差异 分配函数返回的整数倍应是 最严格的基本对齐要求([基本对齐]) 大小不大于数组大小的对象类型 创建。[ 注意:因为假设分配函数返回 指向与任何对象适当对齐的存储的指针 具有基本对齐的类型,此约束对数组分配 开销允许将字符数组分配给 稍后将放置哪些其他类型的对象。 — 尾注 ]
对我来说,这读起来就像新表达式必须在分配函数返回的确切地址处创建一个对象(假设它不是数组类型)。由于您使用的是内置的放置 new,这将带我们了解以下内容
-
[新建.删除.放置]
这些功能是保留的,C++程序可能无法定义功能 替换标准C++库中的版本 ([约束])。([basic.stc.dynamic])的规定不适用 到这些保留的放置形式为运算符新建和运算符删除。
void* operator new(std::size_t size, void* ptr) noexcept;
返回:PTR。
备注:故意不执行其他操作。
这保证了传递给表达式的地址是您分配的字符数组对象的确切地址。这是因为转换为
void*
不会更改源地址。
我认为保证地址相同就足够了,即使指针通常不可互换。因此,根据 [expr.eq]/1(感谢 @T.C.):
相同类型的两个指针比较相等当且仅当它们是 两者都为 null,都指向同一函数,或者都表示相同的函数 地址([基本化合物])。
比较必须产生 true,同样是因为地址相同。
我可以假设
(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; }
};
如果您希望此新表达式调用标准非分配放置新运算符,则代码需要
- 包括标头
<new>
。 - 确保传递一个
void*
参数。 - 在它前面加上范围解析运算符
::
以绕过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));
在这种情况下,确实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)...);
}