C++之内存管理建议收藏

内存分配方式在C++中,内存分为内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。(1)堆就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

全栈程序员社区此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“全栈程序员社区”或者“www_javaforall_cn”或者微信扫描右侧二维码都可以关注本站微信公众号。

  内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存泄漏几乎在每个C++程序中都会发生,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,还放弃了C++超绝的性能。

内存分配方式

  在C++中,内存分为内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

(1)堆

  就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

(2)栈

  在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3)自由存储区

  就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

(4)全局/静态存储区

  全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

(5)常量存储区

  这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。

1. 堆和栈的区别

(1)管理方式

  对于栈来讲,是由编译器自动管理,无需我们手工控制;

  对于堆来说,释放工作由程序员控制,容易产生memory leak;

(2)空间大小

  一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的;

  但是对于栈来讲,一般都是有一定的空间大小的,我们可以修改;

(3)生长方向

  对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;

  对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长;

(4)分配方式

  堆都是动态分配的,没有静态分配的堆;

  栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。

  动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

(5)分配效率

  栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高;

  堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低

(6)能否产生碎片

  对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低;

  对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列;

控制C++内存分配

  在嵌入式系统中使用C++的一个常见问题是内存分配,即对new 和 delete 操作符的失控。

  具有讽刺意味的是,问题的根源却是C++对内存的管理非常的容易而且安全。具体地说,当一个对象被消除时,它的析构函数能够安全的释放所分配的内存。

  这当然是个好事情,但是这种使用的简单性使得程序员们过度使用new 和 delete,而不注意在嵌入式C++环境中的因果关系。并且,在嵌入式系统中,由于内存的限制,频繁的动态分配不定大小的内存会引起很大的问题以及堆破碎的风险。

  作为忠告,保守的使用内存分配是嵌入式环境中的第一原则。

  但当你必须要使用new 和delete时,你不得不控制C++中的内存分配。你需要用一个全局的new 和delete来代替系统的内存分配符,并且一个类一个类的重载new 和delete。

  一个防止堆破碎的通用方法是从不同固定大小的内存持中分配不同类型的对象。对每个类重载new 和delete就提供了这样的控制。

1. 为单个类重载new和delete操作符

class TestClass {
public:
    TestClass()
    {
        cout << "TestClass()" << endl;
    }
    ~TestClass()
    {
        cout << "~TestClass()" << endl;
    }
    void * operator new(size_t size);
    void operator delete(void *p);
};

void *TestClass::operator new(size_t size)
{
    cout << "operator new" << endl;
    void *p = malloc(size);
    return (p);
}

void TestClass::operator delete(void *p)
{
    cout << "operator delete" << endl;
    free(p); 
}

void main()
{
    TestClass *pTest = new TestClass;
    delete pTest;
    pTest = NULL;
}

C++之内存管理建议收藏

2. 为单个类重载new[]和delete[]操作符

class TestClass {
public:
    TestClass()
    {
        cout << "TestClass()" << endl;
    }
    ~TestClass()
    {
        cout << "~TestClass()" << endl;
    }
    void * operator new[](size_t size);
    void operator delete[](void *p);
};

void *TestClass::operator new[](size_t size)
{
    cout << "operator new" << endl;
    void *p = malloc(size);
    return (p);
}

void TestClass::operator delete[](void *p)
{
    cout << "operator delete" << endl;
    free(p); 
}

void main()
{
    TestClass *pTest = new TestClass[5];
    delete[] pTest;
    pTest = NULL;
    return;
}

C++之内存管理建议收藏

特别特别要注意size_t nSize的大小!

C++new和delete实现原理:
https://blog.csdn.net/passion_wu128/article/details/38966581

常见的内存错误及其对策

1. 常见内存错误

  (1) 内存分配未成功,却使用了它

  (2)内存分配虽然成功,但是尚未初始化就引用它

  (3)内存分配成功并且已经初始化,但操作越过了内存的边界

  (4)忘记了释放内存,造成内存泄露

  (5) 释放了内存却继续使用它

2. 对策

  (1)用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存

  (2)不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用

  (3)避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作

  (4)动态内存的申请与释放必须配对,防止内存泄漏

  (5)用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120161.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【交换机】MAC-VLAN的功能作用是什么,如何设置[通俗易懂]

    【交换机】MAC-VLAN的功能作用是什么,如何设置[通俗易懂]MACVLAN就是基于MAC地址划分的VLAN,MACVLAN的最大优点就是用户不需要固定在某些端口下,可以随意移动,比如当用户物理位置移动时,即从一台交换机换到其它的交换机时,VLAN不用重新配置,所以,可以认为这种根据MAC地址的划分VLAN方法是基于用户的MAC地址信息来的。MACVLAN的缺点是初始化时,所有的用户都必须进行配置MAC与VLAN的对应关系。主要有两个配置步骤:1、所

    2022年8月10日
    20
  • python tkinter库 密码实时显示_python tkinter库实现气泡屏保和锁屏

    python tkinter库 密码实时显示_python tkinter库实现气泡屏保和锁屏本文实例为大家分享了pythontkinter库实现气泡屏保和锁屏的具体代码,供大家参考,具体内容如下显示效果如下:代码:importrandomimporttkinterimportthreadingfromctypesimport*classRandomBall(object):”””定义关于球的类”””def__init__(self,canvas,screen_wid…

    2022年7月21日
    14
  • PHP进销存erp源码库存管理系统

    PHP进销存erp源码库存管理系统PHP进销存erp源码库存管理系统(2次开发另外收费)本系统开发PHP+MySQL采用CI2.x框架本系统运行环境php5.3+mysql5.5支持IIS、apache不支持nginx源码网站:www.phprr.com演示地址:http://www.phprr.com/show-55账号:admin密码:jxc888888…

    2022年5月22日
    35
  • IDEA 2018 2020 2021 2022 各版本对Maven版本兼容问题汇总「建议收藏」

    IDEA 2018 2020 2021 2022 各版本对Maven版本兼容问题汇总「建议收藏」IDEA2022兼容maven3.8.1及之前的所用版本IDEA2021兼容maven3.8IDEA2018202020212022各版本对Maven版本兼容问题汇总Maven3.6.3版本兼容问题错误信息如下:1.IDEA2021兼容maven3.8.1及之前的所用版本2.IDEA2020兼容Maven3.6.3及之前所有版本3.IDEA2018兼容Maven3.6.1及之前所有版本IDEA2022兼容maven3.8.1及之前的所用版本

    2022年8月22日
    79
  • Hibernate二级缓存的使用「建议收藏」

    Hibernate二级缓存的使用「建议收藏」一、Hibernate共有两级缓存        Session级别缓存—-一级缓存(事务范围)        SessionFactory级别缓存—-二级缓存(进程范围) SessionFactory级别缓存        内置:Hibernate自带的,不可卸载.通常在Hibernate的初始化阶段,Hibernate会把映射

    2022年5月23日
    37
  • Ubuntu下使用IntelliJ IDEA的正确姿势

    Ubuntu下使用IntelliJ IDEA的正确姿势

    2022年3月7日
    34

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号