#include <iostream> class T { int x; public: T() { std::cout << "constr\n"; } ~T() { std::cout << "destr\n"; } }; int main (void) { char *buf = new char[sizeof(T)]; //13 line T *t = new(buf) T; t->~T(); } 

What kind of memory allocation is new(buf) ? That is, memory is allocated in line 13, and then in line 14 no new memory is allocated for an object of type T , but the already allocated memory from that pointed to by buf is taken. That is, this method takes less action, because you can allocate a lot of memory once and then use it with the help of new(buf) when dynamically allocating objects. If this is the case, then the pointer will point to the next free memory cell of the selected area, when, for example, there is another allocation T *t2 = new(buf) T; that is, buf points to a chunk of space about the size of a class T and therefore overwrites the memory pointed to by *t . And yet it is not clear how this is implemented. Since new is an operator, there probably is its overloaded version, which takes a pointer to the memory already allocated and the second parameter is the object that needs to be allocated in it. In general, this is all speculation and maybe I'm completely wrong about this. So it would be best if you wrote what they called it, so that you could at least read about this method somewhere or explain it as you see fit.

  • 6
  • 2
    @mzarb, why did you decide that you can allocate a lot of memory once and then use it with the help of new (buf) when dynamically allocating objects. Where does this multiplicity come from? In fact, T * t = new (buf) T; instructs to create one instance of object T in memory at the address contained in the pointer buf . So dynamic memory for T in this new will not be allocated and the destructor must be called manually, before delete [] buf. - avp
  • one
    @avp: maybe the vehicle meant allocating a large arena and creating objects on it at different offsets (a technique used to speed up memory allocation). - VladD
  • 2
    @mzarb, in the code T * t = new (buf) T; memory is NOT allocated. This code tells the compiler to instantiate T by the SEND in the argument new() address. This was already mentioned in the comments. And new returns the address buf (you can check). At the same time look at addresses with char buf [sizeof (T)]; - avp
  • 2
    Everything is more complicated here. In the case of T t() , the default constructors for all fields will be called. In the case of int, the zero will simply be written, in the case of std :: string, its constructor will be called (it is much more complicated inside). If there is a call T t , then there are two options. If the object has a default constructor ( T() ), then it will be called. If it is not, then no one will be reset. Here are three helpful links 1 , 2 and 3 . - KoVadim

1 answer 1

@mzarb , there really is no magic here. It's pretty simple. That you endow the language / compiler with some special properties.

Disassemble an example of your (slightly modified) program.

 avp@avp-xub11:~/hashcode$ cat mzarb.cpp #include <iostream> #include <string.h> class T { int x; public: T() { std::cout << "constr\n"; } ~T() { std::cout << "destr\n"; } void setX(int v) { x = v; } }; int main (void) { struct { char f1[2]; char f2[10]; } s; strcpy (s.f2,(char *)"1234"); std::cout << "f2 before: " << s.f2 << '\n'; T *t = new(s.f1) T; std::cout << "f2 after: " << s.f2 << '\n'; t->setX('z'); std::cout << "f2 again: " << s.f2 << '\n'; std::cout << "f2 offset: " << s.f2+2 << '\n'; t->~T(); } avp@avp-xub11:~/hashcode$ g++ mzarb.cpp avp@avp-xub11:~/hashcode$ ./a.out f2 before: 1234 constr f2 after: 1234 f2 again: f2 offset: 34 destr avp@avp-xub11:~/hashcode$ 

See, everything is extremely transparent. No one checks anything. And the reasoning about reliability and safety is in reality “divorce of suckers”.

Yes, the cross compiler does a lot of checks and for them the language makes you write (and learn) a lot of superfluous.

  • > T *t = new(s.f1) T; Is there an object on the stack being created? - mzarb
  • one
    @mzarb, yes on the stack. Please note, f2 after: 1234 no initialization of the memory of the object (really one int) occurs. - avp
  • @avp> The compiler to refer to the x field of the created instance t of class T will generate machine instructions that use the address in the variable buf. Thank you, this did not know either. But how is the address returned by new taken as an object? For example, the код new T() will look like "address_which_new_new ((T ()))", but to create an object you need the same type before the object itself, that is, there must be something such "T ad_object_which_vert_new ()", by analogy with the creation of a regular object without new "T t ()", where in the place of "t" would be the address in the case of creation through new. - mzarb
  • one
    @mzarb, the address that new returns will be written to the variable t . You and the C ++ compiler perceive it as an object during the compilation of the program text. The creation of an object is very loud words, behind which is the allocation of a section of memory of the desired size (in some cases its initialization) and the generation of commands for calling the appropriate constructor. During execution, there is no information about the types (in our case). All addresses are just integers 32 bits long (or 64 on a 64-bit computer). - Yes, IMHO, you must first master C and assembler (and you can stop at this). - avp
  • @avp> new "T t ()" Here I meant not new T t() , but T t() . It is just not clear what happens after new returns the address, that is, what the code will look like (from the C ++ perspective, not deeper) in which the constructor is called with this address, and not what happens in the constructor after the call. - mzarb