• C++内存管理


    C++内存管理

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int main()
    {
        /**
         * c++中内存获得/释放的几种方式
         * malloc()                     free()
         * new                          delete
         * ::operator new()             ::operator delete()
         * allocator<T>::allocate()     allocator<T>::deallocate()
            当然你也可以调用操作系统API获得内存,但是这样程序就不具备可移植性
         * */
    
        //  初步使用
    
        //  [1] malloc free
        void *p1 = malloc(512);     //  512byte
        free(p1);
    
        // [2]
        complex<int>* p2 = new complex<int>;
        delete p2;
    
        // [3] 这是一种特殊的指针,底层也是调用free和malloc
        void* p3 = ::operator new(512); //512 byte
        ::operator delete(p3);
    
        // [4] 分配器 STL标准库的内容,在各个环境下可能不同, 我这里是clang
        int *p4 = allocator<int>().allocate(5);  //VC下有第二个参数(int*)0
        allocator<int>().deallocate(p4, 5);
    
        //gnu下是 alloc::allocate(512) (这里是字节了) 上面是几个int 和 alloc::deallocator
        //alloc 这个东西是老的版本,在新版还了个名字
        //有点麻烦,还要告知还多少内存。
    
        //===========================================
    
        return 0;
    }
    

    证明new底层是malloc,大概是这个画风,在VC6的标准库里面畅游。。。

    void * operator new( unsigned int cb )
    {
        void *res = _nh_malloc( cb, 1 );
    
        return res;
    }
    
    
    void * __cdecl _nh_malloc_base (size_t size, int nhFlag)
    {
            void * pvReturn;
    
            //  validate size
            if (size > _HEAP_MAXREQ)
                return NULL;
    
    #ifndef WINHEAP
            /* round requested size */
            size = _ROUND2(size, _GRANULARITY);
    #endif  /* WINHEAP */
    
            for (;;) {
    
                //  allocate memory block
                if (size <= _HEAP_MAXREQ)
                    pvReturn = _heap_alloc_base(size);
                else
                    pvReturn = NULL;
    
                //  if successful allocation, return pointer to memory
                //  if new handling turned off altogether, return NULL
    
                if (pvReturn || nhFlag == 0)
                    return pvReturn;
    
                //  call installed new handler
                if (!_callnewh(size))
                    return NULL;
    
                //  new handler was successful -- try to allocate again
            }
    }
    
    

    上面介绍了几种c++获得内存的方式的使用方式

    当我们需要内存的时候,可以用mmap等系统调用直接向操作系统索取内存。但是这样就不具备可移植性。

    于是就出现了malloc函数,由这个函数去实现底层内存的索取,我们只管要即可。

    在c++面向对象出来后,我们如果需要用malloc出对象,需要手动调用构造函数。是比较麻烦的,但是malloc是函数,不在编译器的控制范围内,于是就有了new。new的作用,调用malloc,把malloc的指针做类型转换,然后调用构造函数,返回

    在c++标准库中,我们使用vector等容器从来不关心内存,是因为内部帮我们实现好了,这就是分配器。

    例如:

    vector的头部

    template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
        class vector : protected _Vector_base<_Tp, _Alloc>
    

    默认使用std::allocator的构造器

    template<typename _Tp>
        class allocator: public __allocator_base<_Tp>
    

    然后这个构造器又继承另一个构造器

    using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
    

    去找这个构造器,发现它是一个别名。最后我们找到了new_allocator这个类,下面是这两个类的两个函数。

    // NB: __n is permitted to be 0.  The C++ standard says nothing
          // about what the return value is when __n == 0.
          pointer
          allocate(size_type __n, const void* = static_cast<const void*>(0))
          {
    	if (__n > this->max_size())
    	  std::__throw_bad_alloc();
    
    #if __cpp_aligned_new
    	if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
    	  {
    	    std::align_val_t __al = std::align_val_t(alignof(_Tp));
    	    return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp), __al));
    	  }
    #endif
    	return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
          }
    
    // __p is not permitted to be a null pointer.
          void
          deallocate(pointer __p, size_type)
          {
    #if __cpp_aligned_new
    	if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
    	  {
    	    ::operator delete(__p, std::align_val_t(alignof(_Tp)));
    	    return;
    	  }
    #endif
    	::operator delete(__p);
          }
    

    它是使用::operator new和::operator delete来申请和释放内存的。

    我们去寻找这两个东西

    /usr/include/c++/7.3.0

    https://en.cppreference.com/w/cpp/header/new

    。。。没有找到我想要的侯捷老师演示的那份原代码。但是operator new的底层会用malloc尝试获取内存,如故没有获得会允许有一个处理http://www.cplusplus.com/reference/new/

    这是标准库的调用内存过程,万物回归到malloc。

    调试过程中的一些记录

    当你malloc一块空间的时候,

        int a = 1;
        int b = 1;
        printf("%x %x
    ", &a, &b);  //41db2e4c 41db2e48 相差四字节
        int *c = new int[1];
        int *d = new int[1];
        printf("%x %x
    ", c, d);    //9d269280 9d2692a0
        void *e = malloc(sizeof(int));
        void *f = malloc(sizeof(int));
        printf("%x %x
    ", e, f);    //aecbd2c0 aecbd2e0
    

    发现这中间的内存间隔是碎片???

        Demo*p = new Demo[3];
        delete[] p;		//delete p    
    
        int *q = new int[3];
        delete q;   // or delete[] q 
    

    如果没有指针的释放操作,new[],搭配delete[],没写【】也不会出错,但是这是不好的写法。

        //placement new 【  new() 】
        char *buf = new char[sizeof(Complex) * 3];
        Complex *pc = new(buf)Complex(1, 2);
        //他没有分配内存,在原来的内存改
        /**
         //上面等价下面部分
         void *mem = operator new(sizeof(Complex), buf);
         pc = static_cast<Complex*>(mem);
         pc->COmplex::Complex(1, 2);  //当然这样调用构造函数不一定总可行
         * */
        delete [] buf;
    

    另一个问题:

    我们malloc出的东西,free操作系统怎么知道多大呢?

    其实,我们要一块空间,这块空间的头尾都会带一些东西。我们可以称之为cookie记录着这块空间的信息。然后打包给我们。

    那么问题来了,我们每次都要一小块空间,空间大小都一样,那么我们为什么要那么多的cookie呢。

    于是就引出了内存池的概念。

    我们一次性分配出一大块空间,重载new和delete,维护一个链表来存储空间,new从链表中取,delete在吧空间抓回链表。这就避免了大量的cookie。gnu下就有类似的代码。找了半天才找到。。。

    #include <bits/stdc++.h>
    #include <c++/ext/pool_allocator.h>
    
    using namespace std;
    
    int main() {
    
        //https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a00015.html
        //http://gcc.gnu.org/onlinedocs/
        vector<int, __gnu_cxx::__pool_alloc<int> >vec;
    
        return 0;
    }
    
  • 相关阅读:
    C++ STL中vector应用
    cocos2dx绘制直线
    [ios] 获得ios设备具体型号
    如何制作 iTunesArtwork
    Ios AppIcon 去掉默认半弧形白色遮罩
    App运行内存打印
    C++ 文件操作
    objc 笔记
    (转)c++ oc字符操作
    cocos2dx 文本 优化
  • 原文地址:https://www.cnblogs.com/Q1143316492/p/10404298.html
Copyright © 2020-2023  润新知