当前位置: 首页 > news >正文

C++内存分配方式

文章目录

  • 1、静态内存分配
  • 2、栈内存分配
  • 3、堆内存分配
  • 4、内存池分配
  • 5、placement new
      • 语法
      • 工作原理
      • 示例
    • `placement new`
      • 应用场景

在C++ 中,内存分配主要有以下几种方式:

1、静态内存分配

  • 特点:在编译时就确定了内存的分配和释放,内存空间是在程序运行前就被预留好的,程序结束时由系统自动释放。
  • 示例:全局变量、静态局部变量都属于静态内存分配。
#include <iostream>

// 全局变量,位于静态存储区
int globalVar = 10;

void func() {
    // 静态局部变量,位于静态存储区
    static int staticVar = 20;
    staticVar++;
    std::cout << "Static variable value: " << staticVar << std::endl;
}

int main() {
    std::cout << "Global variable value: " << globalVar << std::endl;
    for (int i = 0; i < 5; i++) {
        func();
    }
    return 0;
}

2、栈内存分配

  • 特点:由编译器自动分配和释放,用于存储函数的参数、局部变量等。其分配和释放速度快,遵循后进先出(LIFO)的原则。但栈的空间大小有限,通常在几KB到几MB之间,如果分配的空间超过栈的大小,就会导致栈溢出。
  • 示例
#include <iostream>

void func() {
    // 局部变量,在栈上分配内存
    int localVar = 30;
    std::cout << "Local variable value: " << localVar << std::endl;
}

int main() {
    func();
    return 0;
}

3、堆内存分配

  • 特点:由程序员手动分配和释放,使用 new 操作符进行内存分配,使用 delete 操作符释放内存。堆内存的大小只受限于系统的虚拟内存大小,相对来说空间比较灵活,但分配和释放的速度比栈内存慢,且如果程序员忘记释放内存,会导致内存泄漏。
  • 示例
#include <iostream>

int main() {
    // 在堆上分配一个整数的内存空间
    int* heapVar = new int;
    *heapVar = 40;
    std::cout << "Heap variable value: " << *heapVar << std::endl;
    // 释放堆内存
    delete heapVar;
    return 0;
}

4、内存池分配

  • 特点:是一种预先分配一定大小内存块的技术,程序在运行过程中需要内存时,直接从内存池中获取,而不是每次都进行系统级的内存分配操作。当使用完内存后,将其归还到内存池中,而不是立即释放回系统。这样可以减少频繁的内存分配和释放操作带来的开销,提高程序的性能和效率,尤其对于大量小对象的频繁分配和释放场景效果显著。
  • 示例:以下是一个简单的内存池示例代码,实现了一个基本的内存池类,用于分配和释放固定大小的内存块。
#include <iostream>
#include <vector>

class MemoryPool {
public:
    MemoryPool(size_t blockSize, size_t numBlocks) : blockSize(blockSize) {
        // 分配内存池的总大小
        char* pool = new char[blockSize * numBlocks];
        // 将每个内存块的地址加入到可用列表中
        for (size_t i = 0; i < numBlocks; i++) {
            freeBlocks.push_back(pool + i * blockSize);
        }
    }

    ~MemoryPool() {
        // 释放内存池的内存
        delete[] freeBlocks[0];
    }

    void* allocate() {
        if (freeBlocks.empty()) {
            std::cerr << "Memory pool exhausted." << std::endl;
            return nullptr;
        }
        // 从可用列表中取出一个内存块
        void* block = freeBlocks.back();
        freeBlocks.pop_back();
        return block;
    }

    void deallocate(void* block) {
        // 将内存块归还到可用列表中
        freeBlocks.push_back(static_cast<char*>(block));
    }

private:
    // 可用内存块的列表
    std::vector<char*> freeBlocks;
    size_t blockSize;
};

int main() {
    // 创建一个内存池,每个内存块大小为16字节,共10个内存块
    MemoryPool pool(16, 10);

    // 从内存池中分配内存
    void* block1 = pool.allocate();
    void* block2 = pool.allocate();

    // 使用内存块...

    // 释放内存块回内存池
    pool.deallocate(block1);
    pool.deallocate(block2);

    return 0;
}

5、placement new

是C++ 中的一种特殊的内存分配方式,它允许在已分配的内存空间上构造对象。以下是关于placement new的详细介绍:

语法

placement new的语法形式为:new (place_address) type (initializers);,其中place_address是一个指向已分配内存的指针,type是要构造的对象类型,initializers是可选的构造函数参数列表。

工作原理

  • placement new不会分配新的内存,而是在给定的地址上直接构造对象。它假定所提供的内存已经足够大,能够容纳要构造的对象,并且该内存未被其他对象占用。
  • 当使用placement new时,编译器会调用对象的构造函数来初始化对象,但不会调用operator new来分配内存。

示例

下面是一个简单的示例,展示了placement new的用法:

#include <iostream>
#include <new>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor called" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor called" << std::endl; }
};

int main() {
    // 分配一块足够大的内存
    void* buffer = operator new(sizeof(MyClass));

    // 使用placement new在分配的内存上构造对象
    MyClass* obj = new (buffer) MyClass();

    // 使用obj

    // 手动调用析构函数
    obj->~MyClass();

    // 释放内存
    operator delete(buffer);

    return 0;
}

placement new

应用场景

  • 内存池管理:在内存池实现中,预先分配一大块内存,然后使用placement new在内存池中分配和构造对象,避免频繁地调用newdelete导致的内存碎片和性能开销。
  • 对象数组的构造:可以分配一个连续的内存块来存储对象数组,然后使用placement new逐个构造数组中的对象。
  • 嵌入对象到特定内存位置:某些情况下,可能需要将对象放置在特定的内存地址,例如与硬件设备交互或遵循特定的内存布局要求,placement new可以满足这种需求。

需要注意的是,使用placement new时,开发者需要负责确保所提供的内存是合适的,并且在对象不再使用时,要手动调用析构函数来释放资源,以避免内存泄漏和未定义行为。

相关文章:

  • 数据结构-排序
  • 使用 request 的 axios 状态码分析
  • 市场热点复盘20240319
  • 数据结构Api
  • 泛微e9 流程表单使用 显示属性联动 实现某个字段的显示与隐藏
  • 【愚公系列】《高效使用DeepSeek》017-知识点思维导图生成
  • 危化品经营单位考试:心理调适与知识巩固双管齐下​
  • 头部颤抖的预防措施
  • win10 如何用我的笔记本 接网线 远程控制 台式机
  • OpenNJet动态API设置accessLog开关,颠覆传统运维工作模式
  • “四预”驱动数字孪生水利:让智慧治水守护山河安澜
  • SIEMENS中集车辆PLM项目建设方案(96页PPT)(文末有下载方式)
  • excel文件有两列,循环读取文件两列赋值到字典列表。字典的有两个key,分别为question和answer。将最终结果输出到json文件
  • LCCI ESG 中英联合认证国际分析师适合的岗位
  • AUTOSAR_DoIP_Detailed
  • 计算机操作系统和进程
  • 基于java的ssm+JSP+MYSQL的高校四六级报名管理系统(含LW+PPT+源码+系统演示视频+安装说明)
  • Windows 11技巧:使用Powershell定义命令别名
  • 2025 年 AI 代码编辑器深度评测:Cursor Pro订阅与解锁自定义 AI 的无限潜能,实战案例全解析
  • 现代复古像素风品牌海报游戏排版设计装饰英文字体 Psygen — Modern Pixel Font
  • 铁路迎来节前出行高峰,今日全国铁路预计发送旅客1870万人次
  • 广东省副省长刘红兵任湖南省委常委、宣传部部长
  • 招行一季度净利372.86亿降2.08%,营收降逾3%
  • 光明网评论员:手机“二次放号”,需要重新确认“你是你”
  • 这场迪图瓦纪念拉威尔的音乐会,必将成为乐迷反复品味的回忆
  • 上海质子重离子医院已收治8000例患者,基本覆盖国内常见恶性肿瘤