1

In his book C++ Programming Language(4th ed), stroustroup has mentioned that the global operator new & delete can be overloaded by writing global functions with the following signatures:

void* operator new(size_t);               // use for individual object
void* operator new[](size_t);             // use for array
void operator delete(void*, size_t);      // use for individual object
void operator delete[](void*, size_t);    // use for array

NOTE: The size_t parameter is passed for the delete to determine the correct object size specifically when deleting a derived object pointed by a base pointer(base needs virtual dtor so that correct size is passed).

I was trying to overload the global versions for the individual object. The operator new works fine. The operator delete with the above signature works fine, but delete doesn't get called. If I change the delete signature so that it just takes a void *, it does get called. What could be the problem:

Here is code:

void * operator new (size_t size)
{
    cout << "My operator new called\n";
    auto p = malloc(size);
    return p;
}

void operator delete (void * ptr, size_t size) // Removing size_t parameter makes it work
{
    cout << "My operator delete called\n";
    free(ptr);
}

Strange is also the fact that if I make the operator delete a member of a class so that its overloaded just for that class, both delete signatures(with size_t and without size_t) seem to work!

Passing size_t parameter in delete does seem logical as explained in the NOTE I had mentioned. But what could be the reason for this behavior? I am using VS2013 for testing out the examples.

Arun
  • 3,138
  • 4
  • 30
  • 41
  • 1
    Specifically, look at the section [overloading `new` and `delete`](https://stackoverflow.com/questions/4421706/operator-overloading/4421791#4421791) – Cory Kramer Jul 16 '14 at 00:07
  • Thanks for the link Cyber. The explanation there seems to suggest that global version of delete has signature which takes two void * and the class specific version takes a void * and a size_t. This is contrary to what is mentioned in the book. So are you suggesting that this is an error in the book? – Arun Jul 16 '14 at 00:13
  • 1
    Read more carefully. Those are placement version you picked out of the post. – Deduplicator Jul 16 '14 at 00:20
  • 2
    See this for the operator `delete` possible signatures: http://en.cppreference.com/w/cpp/memory/new/operator_delete – vsoftco Jul 16 '14 at 00:22
  • @Deduplicator Aaah...yes...so the global ones don't need a size_t. So the book has an error I suppose – Arun Jul 16 '14 at 00:32
  • @vsoftco thanks for the link. So all will make sense when C++14 is supported in VS! The book was correct, the compiler was just not conformant :-) – Arun Jul 16 '14 at 00:34
  • @Arun, it is written by Bjarne :) – vsoftco Jul 16 '14 at 00:39
  • No error in the book, just a standard mismatch – Deduplicator Jul 16 '14 at 01:19

2 Answers2

1

From the C++1y draft:

5.3.5 Delete [expr.delete]

[...]
11 When a delete-expression is executed, the selected deallocation function shall be called with the address of the block of storage to be reclaimed as its first argument and (if the two-parameter deallocation function is used) the size of the block as its second argument.83

Footnote 83) If the static type of the object to be deleted is complete and is different from the dynamic type, and the destructor is not virtual, the size might be incorrect, but that case is already undefined, as stated above.

17.6.4.6 Replacement functions [replacement.functions]

1 Clauses 18 through 30 and Annex D describe the behavior of numerous functions defined by the C++ standard library. Under some circumstances, however, certain of these function descriptions also apply to replacement functions defined in the program (17.3).
2 A C++ program may provide the definition for any of twelve dynamic memory allocation function signatures declared in header <new> (3.7.4, 18.6):

operator new(std::size_t)
operator new(std::size_t, const std::nothrow_t&)
operator new[](std::size_t)
operator new[](std::size_t, const std::nothrow_t&)
perator delete(void*)
operator delete(void*, const std::nothrow_t&)
operator delete[](void*)
operator delete[](void*, const std::nothrow_t&)

note by me: The next four are new in C++1y

operator delete(void*, std::size_t)
operator delete(void*, std::size_t, const std::nothrow_t&)
operator delete[](void*, std::size_t)
operator delete[](void*, std::size_t, const std::nothrow_t&)

3 The program’s definitions are used instead of the default versions supplied by the implementation (18.6). Such replacement occurs prior to program startup (3.2, 3.6). The program’s definitions shall not be specified as inline. No diagnostic is required.

Also take a look at the proposal which introduces sized deallocation in C++1y:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
1

In C++11, non-member void operator delete(void*, size_t) is "placement deallocation with additional arguments". It corresponds to the placement allocation with additional arguments (if you defined one): void *operator new(size_t, size_t).

Clarification of this, according to 3.7.4.2, T::operator delete(void*, size_t) is a usual deallocation function, but N3337 does not say that ::operator delete(void *, size_t) is a usual deallocation function; in fact that signature for ::operator delete does not appear anywhere in the document. Specifically, 17.6.4.6 does not list it amongst the global versions.

In C++1y, ::operator delete(void*, size_t) is a usual deallocation function (i.e. non-placement). It seems to me at this is a breaking change between C++11 and C++1y.

According to N3797, in C++1y if you replace operator delete(void *) then you must also replace operator delete(void *, size_t) and vice versa. (Otherwise, presumably, the program is ill-formed).

Also according to N3797, either of those two functions may be called for deallocation, but I don't see a clear specification of in which circumstances which function is called.

Note: overloading is the wrong term; when you define a usual allocation operator, it replaces the standard library version.

M.M
  • 138,810
  • 21
  • 208
  • 365