3

See the following code:

#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;
}

output for above code:

  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.

My question is :

1)Why nothrow version of delete is not called.

2)can i use size parameter in nothrow version of delete. like

  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)What is use of nothrow version of delete.

4)I used same code in both version new and new[] , delete and delete[] but one is normal variable and another one is array variable how this ?.

5)The compiler gives warning : 'operator new' must not return NULL unless it is declared 'throw()' (or -fcheck-new is in effect) How to over come that?

srilakshmikanthanp
  • 2,231
  • 1
  • 8
  • 25
  • Please put the linebreaks at the end of the line. Firstly, this makes output more readable. Secondly, it flushes the buffers in the stream, so that output becomes visible immediately. – Ulrich Eckhardt Dec 17 '19 at 07:43
  • 2
    @UlrichEckhardt `\n` does not flush buffers (necessarily). Only `std::endl` will do that. – underscore_d Dec 17 '19 at 09:51
  • True, it doesn't necessarily. In this case, it does though, because `std::cout` is synced with C stdio streams (by default) and stdout is line-buffered (by default). Using `std::endl` or perhaps the even more explicit `std::flush` is indeed better though. – Ulrich Eckhardt Dec 17 '19 at 23:36

2 Answers2

5

The default behavoir of new

C++ throws bad_alloc exception on failing new unless you explicitly call new(std::nothrow).

This is not affected by the question of whether or not the user overloaded operator new.

Thus, when you call new without passing std::nothrow you will not reach the nothrow version. Even if you overloaded one.


new(nothrow)

Note that your nothrow version does throw, which is not the way to go. It is supposed to look like:

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
}

This however wouldn't change the behavior explained: to reach the nothrow version of new you must call it explicitly as you indeed see in your code.


Disabling exceptions

There are compilers which allow "disabling" exceptions, e.g. the flag --exceptions in gcc. This is compiler dependent but in most cases, if not all - putting aside old versions of MSVC - disabling exceptions do not cause exceptions not to be thrown but only make the compiler assume they wouldn't be thrown, and a call to new would still throw if it fails.

See: What exactly will happen if I disable C++ exceptions in a project?

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


Relevant SO questions:

Can also be a good read:


operator delete

The delete operator should never throw an exception. Both versions of it, the nothrow and the normal (non-nothrow).

So what is the usage of the nothrow version of operator delete?

If new was called to allocate an object, memory was successfully allocated but then the constructor of this object threw an exception, the new operation shall fail, propagating the exception thrown by the constructor. But before that there is a need to deallocate the memory obtained. This is done automatically by calling the delete operator. If the new which just failed was the nothrow version then the delete operator to be called would be the nothrow one, otherwise it would be the normal one. Note that both versions of the delete operator do not and should not throw. Also note that you cannot call the nothrow version of the delete operator on your own! But you can create a scenario in which it would be called.

Example:

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
    }
}

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

See:

Amir Kirsh
  • 12,564
  • 41
  • 74
1

If you haven't already, you should look at the cppreference page for operator delete, as already mentioned.

Notice that 'standard' sized delete operators are

  1. only available in C++14 and later and
  2. only for the 'usual operators' (that is, the ones at global scope).

For completeness, C++17 has added a set of overloads to new and delete for overaligned memory. I haven't yet looked at the C++20 destroying delete overloads.

It doesn't make much sense to have a class-specific sized delete overload as you can easily access the size of the object (with sizeof ex). Well, maybe for the array version there could be some use.

You can always explicitly call your overloads, e.g., something like

ex::operator delete(pt, foo);
Paul Floyd
  • 5,530
  • 5
  • 29
  • 43