请考虑以下代码和以下问题:
/*
* GCC 4.4
*/
#include <iostream>
using namespace std;
class A {
public:
void* operator new(size_t s) {
cout << "A::operator new(size_t) called\n";
}
void operator delete(void* p) {
cout << "A::operator delete(void*) called\n";
}
void* operator new(size_t s, A* p) {
cout << "A::operator new(size_t, A*) called\n";
}
void operator delete(void* p, size_t s) {
cout << "A::operator delete(void*, size_t) called\n";
}
};
void* operator new(size_t s) {
cout << "::operator new(size_t) called\n";
}
void operator delete(void* p) {
cout << "::operator delete(void*) called\n";
}
void* operator new(size_t s, A* p) {
cout << "::operator new(size_t, A*) called\n";
}
void operator delete(void* p, size_t s) {
cout << "::operator delete(void*, size_t) called\n";
}
int main() {
A* p1 = new A(); // See question 1.
delete p1; // See question 2.
A* p2 = new (p1) A(); // See question 3.
delete p2; // See question 4.
}
下面的问题似乎有点多余。但是,我想要区分的是C ++标准规则从实现定义的内容中定义的内容。
operator new(size_t)
将在任何情况下使用(取自A或来自全球
命名空间,无论是否默认)。还行吧。
现在尝试仅删除void* A::operator new(size_t) {}
:为什么编译器会给出:
错误:没有匹配函数来调用'A :: operator new(unsigned INT)” 注意:候选者是:static void * A :: operator new(size_t,A *)
Connot编译器从全局中获取::operator new(size_t)
命名空间?
为什么operator delete(void*)
首选operator delete(void*, size_t)
(当这两个版本都存在于取自operator delete (void*)
的同一名称空间中时)?
如果删除void* A::operator new(size_t, A*)
,为什么代码不会编译,
虽然在全局命名空间中定义了相同版本的运算符?
错误:没有匹配函数来调用'A :: operator new(unsigned int, A *&安培)” 注意:候选者是:static void * A :: operator new(size_t)
为什么编译器仍然喜欢
operator delete (void*)
,虽然已使用A::operator new(size_t, A*)
获得p2?
答案 0 :(得分:2)
关于你的第一个和第三个问题,IMO就是这样的:
如果您明确说明::new
,则不应该有任何问题。如果编译器无法在类中找到新运算符的专用版本,则它只会转到全局命名空间。
从标准:§5.3.4,
9。如果new-expression以一个一元的::运算符开头,则在该表达式中查找分配函数的名称。 全球范围。否则,如果分配的类型是类类型T或其数组,则为分配函数 在T的范围内查找名称。如果此查找未能找到名称,或者分配的类型不是 类类型,在全局范围内查找分配函数的名称。
答案 1 :(得分:2)
让我们想象一些场景。首先,以下总是有效:
A * p1 = ::new A;
::delete p1;
A * p2 = ::new (addr) A; // assume "void * addr" is valid
p2->~A();
全局表达式总是使用全局命名空间中的相应运算符,所以我们没问题。请注意,没有“placement-delete”表达式。必须通过调用析构函数来明确销毁每个放置构造的对象 。
接下来,假设我们写道:
A * p3 = new A;
delete p3;
这一次,首先在operator new(size_t)
的范围中查找分配函数A
。该名称存在,但如果删除正确的重载,则会出错。 (这回答了Q1和Q3。)同样适用于operator delete()
。单参数版本((void *)
)优于双参数版本((void *, size_t)
)没有特别的原因;你应该只有两个中的一个。 (另请注意,双参数函数没有全局版本。)
最后,让我们重温一下展示新表达式。由于没有放置 - 删除表达式,您的最后一行代码是错误(未定义的行为):您不能delete
任何未通过默认值获得的内容 - {{ 1}}表达。如果定义了placement-new分配函数,则还应定义匹配的释放函数。但是,只有在某个特定情况下才会自动调用此函数:如果placement-new expression new
导致构造函数抛出异常,则然后将调用相应的释放函数。否则,由于所有放置构造都是手动的,您通常只会手动调用放置释放功能(通常根本不会,因为它很少做任何实际工作)。
典型情况可能是这样的:
new (a, b, c) Foo;
要完整地查看开头代码示例,请注意标准要求全局void * addr = ::operator new(sizeof(Foo)); // do real work
Foo * p = new (addr, true, 'a') Foo; // calls Foo::operator new(void*, bool, char);,
// then calls the constructor Foo::Foo()
// in case of exception, call Foo::operator delete(addr, true, 'a')
p->~Foo();
Foo::operator delete(addr, true, 'a'); // rarely seen in practice, often no purpose
::operator delete(addr); // do real work
不执行任何操作。也就是说,全球布局新需要零清理。
答案 2 :(得分:0)
对象不记得它是如何创建的;放置删除仅在相应的放置新抛出时使用,否则使用常规删除操作符。