简体   繁体   English

关于operator new()和operator delete()的问题

[英]Questions about operator new() and operator delete()

Consider the following code and the questions below: 请考虑以下代码和以下问题:

/*
* 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.
}

The questions below might seem redundant somehow. 以下问题似乎有点多余。 However, what I am trying to distinguish is what is defined by the C++ standard rules from what is defined by the implementation. 但是,我想要区分的是C ++标准规则从实现定义的内容中定义的内容。

  1. operator new(size_t) will be used in any case (taken from A or from the global namespace, be it default or not). operator new(size_t)将在任何情况下使用(取自A或来自全局命名空间,无论是否默认)。 This is OK. 还行吧。 Now try to remove void* A::operator new(size_t) {} only: why does the compiler gives: 现在尝试仅删除void* A::operator new(size_t) {} :为什么编译器会给出:

    error: no matching function for call to 'A::operator new(unsigned int)' note: candidates are: static void* A::operator new(size_t, A*) 错误:没有匹配函数来调用'A :: operator new(unsigned int)'注意:候选者是:static void * A :: operator new(size_t,A *)

    Connot the compiler pick up ::operator new(size_t) from the global namespace? Connot编译器从全局命名空间中获取::operator new(size_t)

  2. Why is operator delete(void*) preferred to operator delete(void*, size_t) (when both of those versions are present in the same namespace where operator delete (void*) is taken from)? 为什么operator delete(void*)首选于operator delete(void*, size_t) (当这两个版本都出现在同一名称空间中,其中operator delete (void*)取自)?

  3. Why will not the code compile if I remove void* A::operator new(size_t, A*) , although there is the same version of the operator defined in the global namespace? 如果我删除void* A::operator new(size_t, A*) ,为什么代码不能编译,尽管在全局命名空间中定义了相同版本的运算符?

    error: no matching function for call to 'A::operator new(unsigned int, A*&)' note: candidates are: static void* A::operator new(size_t) 错误:没有匹配函数来调用'A :: operator new(unsigned int,A *&)'注意:候选者是:static void * A :: operator new(size_t)

  4. Why does the compiler still prefer operator delete (void*) , although A::operator new(size_t, A*) has been used for getting p2? 为什么编译器仍然喜欢operator delete (void*) ,尽管A::operator new(size_t, A*)已用于获取p2?

As to your first and third question, it IMO works like that: 关于你的第一个和第三个问题,IMO就是这样的:

  1. Because you didn't use ::new, the compiler tries to find a new operator in the A class. 因为您没有使用:: new,所以编译器会尝试在A类中查找新的运算符。
  2. It finds one, but it cannot find the proper overload, so it fails. 它找到一个,但它找不到正确的重载,所以它失败了。

If you explicitly state ::new , it shouldn't have any problem. 如果你明确陈述::new ,它应该没有任何问题。 The compiler only goes to the global namespace if it cannot find the specialized version of the new operator in the class. 如果编译器无法在类中找到新运算符的专用版本,则它只会转到全局命名空间。

From the standard: § 5.3.4, 从标准:§5.3.4,

9. If the new-expression begins with a unary :: operator, the allocation function's name is looked up in the global scope. 9.如果new-expression以一元::运算符开头,则在全局范围中查找分配函数的名称。 Otherwise, if the allocated type is a class type T or array thereof, the allocation function's name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up in the global scope. 否则,如果分配的类型是类类型T或其数组,则在T的范围内查找分配函数的名称。如果此查找未能找到名称,或者如果分配的类型不是类类型,则分配函数的名称在全局范围内查找。

Let's imagine some scenarios. 让我们想象一些场景。 First off, the following always works: 首先,以下内容始终有效:

A * p1 = ::new A;
::delete p1;

A * p2 = ::new (addr) A;        // assume "void * addr" is valid
p2->~A();

The global expressions always use the corresponding operators from the global namespace, so we're fine. 全局表达式总是使用全局命名空间中的相应运算符,所以我们没问题。 Note that there is no "placement-delete" expression . 请注意, 没有“placement-delete”表达式 Every placement-constructed object must be destroyed explicitly by calling the destructor. 必须通过调用析构函数显式地销毁每个placement-construct对象。

Next up, suppose we write: 接下来,假设我们写道:

A * p3 = new A;
delete p3;

This time round, the allocation function operator new(size_t) is looked up in the A 's scope first. 这一次,首先在A的范围中查找分配函数operator new(size_t) The name exists, but if you remove the correct overload, you have an error. 该名称存在,但如果删除正确的重载,则会出错。 (This answers Q1 and Q3.) The same goes for operator delete() . (这回答了Q1和Q3。)同样适用于operator delete() There's no particular reason why the one-parameter version ( (void *) ) is preferred over the two-argument version ( (void *, size_t) ); 单参数版本( (void *) )优于双参数版本( (void *, size_t) )没有特别的原因; you should just have one of the two. 你应该只有两个中的一个。 (Note also that there's no global version of the two-argument function.) (另请注意,双参数函数没有全局版本。)

Finally, let us revisit placement-new expressions. 最后,让我们重温一下展示新表达式。 Since there is no placement-delete expression , your final line of code is an error (undefined behaviour): You mustn't delete anything that wasn't obtained through a default- new expression. 由于没有放置 - 删除表达式 ,因此最后一行代码是错误(未定义的行为):您不能delete任何未通过默认new表达式获取的内容。 If you define a placement-new allocation function, you should also define the matching deallocation function. 如果定义了placement-new分配函数,则还应定义匹配的释放函数。 This function will only be called automatically on one specific situation, though: If a placement-new expression new (a, b, c) Foo; 这个函数只会在一个特定情况下自动调用,但是:如果一个placement-new表达式new (a, b, c) Foo; causes the constructor to throw an exception, then the corresponding deallocation function is called. 导致构造函数抛出异常, 然后调用相应的释放函数。 Otherwise, since all placement-construction is manual, you will typically only call the placement deallocation function manually (and often never at all, because it rarely does any actual work). 否则,由于所有放置构造都是手动的,因此通常只会手动调用放置释放功能(通常根本不会,因为它很少做任何实际工作)。

The typical scenario might be something like this: 典型情况可能是这样的:

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

To come full circle to the opening code example, note that the standard demands that the global ::operator delete(void *, void *) do nothing. 要完整地讨论开头代码示例,请注意标准要求global ::operator delete(void *, void *)不执行任何操作。 That is, global placement-new is required to need zero clean-up. 也就是说,全球布局新需要零清理。

An object does not remember how it was created; 对象不记得它是如何创建的; placement delete is only used when the corresponding placement new throws, otherwise the regular delete operator is used. placement delete仅在相应的placement new throws时使用,否则使用常规delete操作符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM