C++内存分配策略

发布时间:2024年01月08日

目录

基础概念

内存布局

分配方式

实现

1.new和delete

2.利用空间配置器alloc

3.用malloc和free

4.静态内存分配


基础概念

在讲内存分配之前,先对一些基础概念进行阐述,以便能更好的讨论问题

内存布局

代码编译为可执行程序后运行占用的内存可以分为以下几个区域:

1.栈区(stack) :? 由编译器自动分配释放,用于存放函数的参数值,局部变量的值等;在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用;栈上的内存在函数返回时就会自动释放;栈区的内存地址延伸方式从高地址向地址;栈内存的大小通常是有限的,所以大量使用可能导致栈溢出。
2.堆区(heap) : 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收;堆可以动态地扩展和收缩,注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3.全局区(静态区、static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4.文字常量区 : 常量字符串就是放在这里的,程序结束后由系统释放。
5. 程序代码区 :?存放函数体的二进制代码,代码里面的全局函数和类成员函数编译后就是存在这个区域。

一个可执行程序,在不运行时占用磁盘空间的是全局区、文字常量区和程序代码区,运行后栈区和堆区在内存里面才划分出来工作。下面是区域划分图:

分配方式

C/C++内存分配方式有3种:

1.从静态存储区域分配:? 是在程序编译和链接时就确定好的内存;这些内存在程序整个运行期间都存在,如全局变量,static变量等。
2.在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限,如数组,局部变量,形参。?
3.从堆上分配:亦称动态内存分配 ,是在程序加载、调入、执行的时候分配/回收的内存;程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定 ,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

实现

1.new和delete

在C++里面这种方式是用的最多的,代码如下:

    template <class T> 
    struct CreateUsingNew
    {
        template <typename... Args>
        static T* Create(Args... args)
        { return new T(std::forward<Args>(args)...); }
        
        static void Destroy(T* p)
        { delete p; }
    };

2.利用空间配置器alloc

在STL中空间配置器的4个函数:

内存的配置:alloc::allocate();
对象的构造:::construct();
对象的析构:::destroy();
内存的释放:alloc::deallocate();

?利用空间分配器分配内存的代码如下:

    template<template<class> class Alloc>
    struct CreateUsing
    {
        template <class T>
        struct Allocator
        {
            static Alloc<T> allocator;

            template <typename... Args>
            static T* Create(Args... args)
            {
                return new (allocator.allocate(1)) T(std::forward<Args>(args)...);
            }

            static void Destroy(T* p)
            {
                //allocator.destroy(p);
                p->~T();
                allocator.deallocate(p,1);
            }
        };
    };

3.用malloc和free

?C语言的传统方式分配和释放内存,代码如下:

    template <class T> 
    struct CreateUsingMalloc
    {
        template <typename... Args>
        static T* Create(Args... args)
        {
            void* p = std::malloc(sizeof(T));
            if (!p) return 0;
            return new(p) T(std::forward<Args>(args)...);
        }
        
        static void Destroy(T* p)
        {
            p->~T();
            std::free(p);
        }
    };

4.静态内存分配

代码如下:

    template <class T> 
    struct CreateStatic
    {
        
#ifdef _MSC_VER
#pragma warning( push ) 
#pragma warning( disable : 4121 )
// alignment of a member was sensitive to packing
#endif // _MSC_VER

        union MaxAlign
        {
            char t_[sizeof(T)];
            short int shortInt_;
            int int_;
            long int longInt_;
            float float_;
            double double_;
            long double longDouble_;
            struct Test;
            int Test::* pMember_;
            int (Test::*pMemberFn_)(int);
        };
        
#ifdef _MSC_VER
#pragma warning( pop )
#endif // _MSC_VER
        
        template <typename... Args>
        static T* Create(Args... args)
        {
            static MaxAlign staticMemory_;
            return new(&staticMemory_) T(std::forward<Args>(args)...);
        }
        
        static void Destroy(T* p)
        {
            p->~T();
        }
    };

上面的例子都用了std::forward,实现了构造函数参数的完美转发,它的用法可以参考博客C++之std::forward-CSDN博客

文章来源:https://blog.csdn.net/haokan123456789/article/details/135465917
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。