Lecture 1: primitives
Four kinds of memory allocation and release
During programming, the memory can be operated directly or indirectly through the above methods. The following describes four C + + memory operation methods:
1.::operator new() calls malloc,::operator delete() calls free
2. malloc and new can usually be used to allocate memory. Of course,:: operator new() (a global function) and allocator allocator can also be used to operate memory. The usage of these functions will be described in detail below. For different compilers, the interface of allocate function is also different:
For GNU C, different versions are different:
In this picture__ gnu_cxx::__pool_alloc().allocate() corresponds to allocator () in the previous figure allocate().
It is very common to allocate memory through malloc and new and release memory through free and delete. It is rare to operate memory through:: operator new. Allocator allocator allocator allocator operation memory is widely used in STL source code, and it is used differently for different compilation environments. The following example is based on the VS2019 environment
#include <iostream> #include <complex> #include <memory>//std::allocator using namespace std; namespace jj01 { void test_primitives1() { cout << "\ntest_primitives().......... \n"; void* p1 = malloc(512);//512 bytes free(p1); complex<int>* p2 = new complex<int>;//one object a unit delete p2; void* p3 = ::operator new(512);//512 bytes ::operator delete(p3); //The following uses allocators provided by the C + + standard library. //Although the interface has standard specifications, the implementation manufacturer does not fully comply with them; The following three forms are slightly different. #ifdef _MSC_VER //VC interface //The following two functions are non static and must be called through object. Three ints are assigned below //Allocator < int > () temporary object int* p4 = allocator<int>().allocate(3, (int*)0); p4[0] = 666; p4[1] = 999; p4[2] = 888; cout << "p4[0] = " << p4[0] << endl; cout << "p4[1] = " << p4[1] << endl; cout << "p4[2] = " << p4[2] << endl; //When returning the memory, the original allocated memory size shows that this kind of thing can only be done by the container allocator<int>().deallocate(p4, 3); #endif // _MSC_VER } } int main(void) { jj01::test_primitives1(); return 0; }
test_primitives().......... p4[0] = 666 p4[1] = 999 p4[2] = 888
Visible int * P4 = allocator() Allocate (3, (int *) 0) operation successfully applied for space of three ints.
new/delete expression of basic components
1. Memory request
The above figure reveals what the compiler does behind the new operation:
1. The first step is to allocate the memory size of a target type through the operator new() operation, here is the size of the Complex;
2. Step 2: pass static_cast casts the obtained memory block into a pointer of the target type. Here is Complex*
3. The third version calls the constructor of the target type, but it should be noted that only the compiler can call the constructor directly through methods such as PC - > complex:: complex (1, 2), which will cause errors.
It is worth noting that inside the operator new() operation, the malloc() function is called.
2. Memory release
Similarly, the first step of the delete operation is to call the destructor of the object, and then release the memory through the operator delete() function. In essence, it also calls the free function.
3. The simulation compiler calls constructors and destructors directly
#include <iostream> #include <string> #include <memory>//std::allocator using namespace std; namespace jj02 { class A { public: int id; A():id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; } A(int i):id(i) { cout << "ctor. this=" << this << " id=" << id << endl; } ~A() { cout << "dtor. this=" << this << " id=" << id << endl; } }; void test_call_all_ctor_directly() { cout << "\ntest_call_ctor_directly().......... \n"; string* pstr = new string; cout << "str= " << *pstr << endl; //! pstr->string::string("jjhou"); //[Error] 'class std::basic_string<char>' has no member named 'string' //! pstr->~string(); // crash -- its language and French meaning are correct. crash is only marked because of the previous line cout << "str= " << *pstr << endl; A* pA = new A(1); cout << pA->id << endl; //1 pA->A::A(3); cout << pA->id << endl; //! pA->A::A(3); //in VC6 : ctor. this=000307A8 id=3 //in GCC : [Error] cannot call constructor 'jj02::A::A' directly A::A(5); //! A::A(5); //in VC6 : ctor. this=0013FF60 id=5 // dtor. this=0013FF60 //in GCC : [Error] cannot call constructor 'jj02::A::A' directly // [Note] for a function-style cast, remove the redundant '::A' cout << pA->id << endl; //in VC6 : 3 //in GCC : 1 delete pA; //simulate new void* p = ::operator new(sizeof(A)); cout << "p=" << p << endl; //p=000307A8 pA = static_cast<A*>(p); pA->A::A(2); //! pA->A::A(2); //in VC6 : ctor. this=000307A8 id=2 //in GCC : [Error] cannot call constructor 'jj02::A::A' directly cout << pA->id << endl; //in VC6 : 2 //in GCC : 0 //simulate delete pA->~A();//Preconstruction ::operator delete(pA);//free() to free memory } } int main(void) { jj02::test_call_all_ctor_directly(); return 0; }
test_call_ctor_directly().......... str= str= ctor. this=0149D1C8 id=1 1 ctor. this=0149D1C8 id=3 3 ctor. this=0118FB48 id=5 dtor. this=0118FB48 id=5 3 dtor. this=0149D1C8 id=3 p=0149FED0 ctor. this=0149FED0 id=2 2 dtor. this=0149FED0 id=2