重载nothrow版本的new和delete

3
请看下面的代码:
#include<iostream>
#include<stdlib.h>
#include<new>

using namespace std;


class ex
{
     int x;
public:
     ex():ex(0){}
     ex(int x):x(x)
     {
     //cout<<"\nConstructor";
     }


 void *operator new(size_t siz)
 {
     void *p;
     cout<<"\nOverloaded new operator normal version";
     p=malloc(siz);
     if(p==NULL)
     {
         bad_alloc ba;
         throw ba;
     }
     else
        return p;
 }
 void operator delete(void *p,size_t sz)
 {
     cout<<"\nOverloaded delete normal version:"<<sz;
     free(p);
 }


 void *operator new(size_t siz,const nothrow_t &tag)
 {
     void *p;
     cout<<"\nOverloaded new operator nothrow version";
     p=malloc(siz);
     if(p==NULL)
     {
        return 0;
     }
     else
        return p;
 }
 void operator delete(void *p,const nothrow_t &tag)
 {
     cout<<"\nOverloaded delete nothrow version";
     free(p);
 }


 void *operator new[](size_t siz)
 {
     void *p;
     cout<<"\nOverloaded new operator normal version in array";
     p=malloc(siz);
     if(p==NULL)
     {
         bad_alloc ba;
         throw ba;
     }
     else
        return p;
 }
 void operator delete[](void *p,size_t sz)
 {
     cout<<"\nOverloaded delete normal version in array:"<<sz;
     free(p);
 }


 void *operator new[](size_t siz,const nothrow_t &tag)
 {
     void *p;
     cout<<"\nOverloaded new operator nothrow version in array";
     p=malloc(siz);
     if(p==NULL)
     {
         return 0;
     }
     else
        return p;
 }
 void operator delete[](void *p,const nothrow_t &tag)
 {
     cout<<"\nOverloaded delete nothrow version in array";
     free(p);
 }

};


int main()
{
ex *pt;

pt=new ex;
delete pt;

pt=new ex[10];
delete[] pt;

pt=new(nothrow) ex;
delete pt;

pt=new(nothrow) ex[10];
delete[] pt;
}

以上代码的输出结果:

  Overloaded new operator normal version
  Overloaded delete normal version:4
  Overloaded new operator normal version in array
  Overloaded delete normal version in array:44
  Overloaded new operator nothrow version
  Overloaded delete normal version:4
  Overloaded new operator nothrow version in array
  Overloaded delete normal version in array:44
  Process returned 0 (0x0)   execution time : 0.724 s
  Press any key to continue.

我的问题是:

1)为什么不会调用nothrow版本的delete。

2)我能在nothrow版本的delete中使用size参数吗,比如:

  void operator delete[](void *p,const nothrow_t &tag,size_t sz);

  or

  void operator delete(void *p,const nothrow_t &tag,size_t sz);

3) nothrow版本的delete有什么用途。

4) 我在new和new[],delete和delete[]中使用了相同的代码,但一个是普通变量,另一个是数组变量,这怎么回事?

5) 编译器会发出警告:'operator new'必须不返回NULL,除非它被声明为'throw()'(或-fcheck-new生效)。如何解决这个问题?


请将换行符放在每行的末尾。首先,这样可以使输出更易读。其次,它会刷新流中的缓冲区,因此输出会立即可见。 - Ulrich Eckhardt
2
@UlrichEckhardt \n 不会强制刷新缓冲区,只有 std::endl 才能做到。 - underscore_d
是的,它并不一定如此。但在这种情况下,确实如此,因为 std::cout 默认与 C 标准 I/O 流同步,而 stdout 默认是行缓冲的。不过,使用 std::endl 或者更明确的 std::flush 更好。 - Ulrich Eckhardt
2个回答

5

new的默认行为

C++在new失败时会抛出bad_alloc异常,除非你显式调用new(std::nothrow)

这与用户是否重载了operator new没有关系。

因此,当你调用new而不传递std::nothrow时,即使你重载了new,你也不能使用nothrow版本。


new(nothrow)

请注意,你的nothrow版本确实会抛出异常,这是不正确的。它应该像下面这样:

void* operator new(size_t size, const nothrow_t& tag) noexcept
{
     void* p = malloc(size);
     return p; // don't throw from the nothrow version of new
}

然而,这不会改变所解释的行为:要达到newnothrow版本,您必须在代码中显式调用它。

禁用异常

有一些编译器允许“禁用”异常, 例如gcc中的标志--exceptions。这是与编译器相关的,但在大多数情况下,除了旧版本的MSVC之外,禁用异常不会导致异常不被抛出,只会使编译器假设它们不会被抛出,如果new调用失败仍然会抛出异常。
参见:如果我在项目中禁用C ++异常,会发生什么?

gcc示例:http://coliru.stacked-crooked.com/a/daa465731e56c681


相关的SO问题:

也可以阅读:


operator delete

delete运算符永远不应抛出异常。两个版本都不应该,即nothrow和正常(非nothrow)。 那么,operator deletenothrow版本的用途是什么? 如果调用new来分配一个对象的内存空间,成功分配了内存空间但是这个对象的构造函数抛出了异常,那么new操作将失败并传播构造函数抛出的异常。在此之前需要释放已获得的内存空间,通过调用delete运算符来实现自动释放。如果刚刚失败的newnothrow版本,则要调用的delete运算符将是nothrow版本,否则将是普通版本。请注意,两种版本的delete运算符都不应该抛出异常。同时注意,你无法手动调用delete运算符的nothrow版本!但是你可以创建一种情况,在这种情况下会被调用nothrow版本。
例子:
struct A {
    A() {
        throw "bahh";
    }
};

void operator delete(void* ptr) noexcept {
    std::cout << "normal delete" << std::endl;
    free(ptr);
}

void operator delete(void* ptr, const std::nothrow_t&) noexcept {
    std::cout << "nothrow delete" << std::endl;
    free(ptr);
}

int main() {
    std::cout << "calling new A" << std::endl;    
    try {
        new A(); // prints: normal delete
    }
    catch(const char* s) {
        std::cout << s << std::endl; // bahh
    }

    std::cout << "calling new(std::nothrow) A" << std::endl;    
    try {
        new(std::nothrow) A(); // prints: nothrow delete
    }
    catch(const char* s) {
        std::cout << s << std::endl; // bahh
    }
}

代码:http://coliru.stacked-crooked.com/a/7be9ce12d251b033

参见:


抱歉,在那一行我犯了一个错误,但我会更改那一行if(p==NULL) { bad_alloc ba; throw ba; } 为 if(p==NULL) { return 0; } 但是,没有调用nothrow版本的delete运算符。 - srilakshmikanthanp
编译器发出警告:除非声明'throw()'(或启用-fcheck-new),否则'operator new'不得返回NULL。 - srilakshmikanthanp
不需要检查 if (p == NULL),你可以直接返回 p;声明 throw() 是 C++98 中 noexcept 的等效语。 - Amir Kirsh
好的,但我没有其他四个问题的想法,你能帮忙解决吗? - srilakshmikanthanp
感谢您的帮助和回答。 - srilakshmikanthanp

1
如果您尚未查看过 operator delete的cppreference页面,建议您现在去了解一下。
请注意,“标准”大小的删除运算符:
1.仅适用于C++14及更高版本; 2.仅适用于“通常的运算符”(即全局作用域中的运算符)。
为了完整起见,C++17已经添加了一组用于新new和delete的超对齐内存的重载。我还没有查看C++20的销毁删除重载。
针对特定类的大小删除重载并不太有意义,因为您可以轻松访问对象的大小(使用sizeof ex)。嗯,也许对于数组版本,可能会有一些用途。
您始终可以显式地调用自己的重载,例如:
ex::operator delete(pt, foo);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接