📜 ⬆️ ⬇️

[C ++] Do we know everything about the new and delete operators?

Hello! Below we will discuss the well-known operators new and delete , more precisely, about what they do not write in books (at least in books for beginners).
Writing this article has led me to a common misconception about new and delete , which I constantly see on the forums and even (!!!) in some books.
Do we all know what new and delete really is? Or just think we know?
This article will help you deal with this (well, and those who know can criticize :))

Note : below we will talk exclusively about the operator new, for other forms of the operator new and for all forms of the operator delete all everything written is also true and can be applied by analogy.

So, let's start with what is usually written in books for beginners when they describe new (the text is taken "from the ceiling", but in general it corresponds to the truth):
The new operator allocates memory greater than or equal to the required size and, unlike C functions, calls the constructor (s) for the object (s) for which the memory is allocated ... you can reload (write to implement somewhere) the new operator to fit your needs.

And for example, they show a primitive overload (implementation) of the operator new, whose prototype looks like this
void* operator new (std::size_t size) throw (std::bad_alloc);

What you want to pay attention to:
1. Nowhere do not share the new key-word of the C ++ language and the new operator, everywhere they are spoken of as an entity.
2. Everywhere they write that new calls constructor (s) for object (s).
Both the first and second is a common misconception.

But let's not hope for books for beginners, let's turn to the Standard, namely to section 5.3.4 and to 18.6.1, in which the topic of this article is actually revealed (or rather, it is revealed).
5.3.4
Try-it-like (8.1) or it can be applied. / * we are not interested further * /
18.6.1
void * operator new (std :: size_t size) throw (std :: bad_alloc);
Effects: The allocation function (5.3.4) to allocate size bytes of
storage suitably aligned to represent any object of that size / * we are not interested further * /

')
Here we already see that in the first case, new is referred to as an expression , and in the second, it is declared as an operator. And these are really 2 different entities!
Let's try to figure out why, so for this we need assembler listings obtained after compiling the code using new. Well, now about everything in order.

new-expression is a language operator, just like if , while , etc. (although if, while , etc. are still referred to as statement , but discard the lyrics) Ie meeting it in the listing compiler generates a specific code corresponding to this operator. Also, new is one of the C ++ key-words , which once again confirms its generality with if 's, for' s, etc. And operator new (), in turn, is simply the C ++ function of the same name, whose behavior can be overridden. IMPORTANT - operator new () does NOT call the constructor (s) for the object (s) for which the memory is allocated. It simply allocates the memory of the right size and that's it. Its difference from sishnyh functions is that it can throw an exception and can override it, as well as make it an operator for a particular class, thereby overriding it only for this class (remember the rest :)).
But new-expression is exactly what the constructor (s) of the object (s) calls. Although it is more correct to say that he, too, does not cause anything, simply by meeting him, the compiler generates the code for calling the constructor (s).

For completeness, consider the following example:

 #include <iostream> class Foo { public: Foo() { std::cout << "Foo()" << std::endl; } }; int main () { Foo *bar = new Foo; } 


after the execution of this code, as expected, “Foo ()” will be printed. Let us see why, for this you need to look into the assembler, which I commented on a little for convenience.
(The code was obtained by the cl compiler used in MSVS 2012, although I mostly use gcc, but this is beside the point)
 /Foo *bar = new Foo; push 1 ;      Foo call operator new (02013D4h) ;  operator new pop ecx mov dword ptr [ebp-0E0h],eax ;  ,   new,  bar and dword ptr [ebp-4],0 cmp dword ptr [ebp-0E0h],0 ;   0    bar je main+69h (0204990h) ;  0,    (   main   - ,    ) mov ecx,dword ptr [ebp-0E0h] ;       ecx (MSVS   this  ecx(rcx)) call Foo::Foo (02011DBh) ;    ;    

For those who do not understand anything, here is (almost) an analogue of what happened on a mutual pseudo-code (i.e., do not try to compile it :))
 Foo *bar = operator new (1); //  1 -   bar->Foo(); //   


The code above confirms everything written above, namely:
1. The (language) operator new and operator new () is NOT the same thing.
2. operator new () does NOT call constructor (s)
3. a call to the constructor (s) is generated by the compiler, encountering key-word "new" in the code

Bottom line: I hope this article has helped you understand the difference between new-expression and operator new () or even find out that it (this difference) exists at all if someone did not know.

The PS operator delete and operator delete () have a similar difference, so at the beginning of the article I said that I would not describe it. I think now you understand why its description does not make sense and can independently verify the validity of what was written above for delete .

Update:
A habrazhitel with the nickname khim in personal correspondence suggested the following code, which well demonstrates the essence of what was written above.
 #include <iostream> class Test { public: Test() { std::cout << "Test::Test()" << std::endl; } void* operator new (std::size_t size) throw (std::bad_alloc) { std::cout << "Test::operator new(" << size << ")" << std::endl; return ::operator new(size); } }; int main() { Test *t = new Test(); void *p = Test::operator new(100); // 100     } 

This code will output the following
 Test::operator new(1) Test::Test() Test::operator new(100) 

which was to be expected.

Source: https://habr.com/ru/post/185662/


All Articles