C++内存管理(2)new、delete详解

这篇具有很好参考价值的文章主要介绍了C++内存管理(2)new、delete详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

new operator(new操作)

new类对象时加不加括号的差别

new工作任务

delete工作任务

new和delete 堆区空间操作(对比malloc和free)

new和delete操作基本类型的空间

new和delete操作基本类型的数组

new和delete操作类的空间

new和delete操作对象数组

new内存分配细节探秘

为什么要尽可能少的调用malloc?

new和delete的重载

为什么要重载 new

监测内存创建销毁,统计和监控泄漏

内存对齐的处理

特定应用:多进程内存共享

重载全局的 new 和 delete

在全局new和delete中添加定制行为

重载类的操作符 new 和 delete

类new和delete操作符重载基础

对齐的内存分配

共享内存的分配

定位new(placement new)

功能

使用placement new

placement new对象的销毁

placement new的应用

硬件编程

实现基础库

多种版本的operator new重载


new operator(new操作)

new类对象时加不加括号的差别

  • A *pa = new A;//有无构造函数初始化为垃圾值
  • A *pa2 = new A();//无构造函数初始化为0,有构造函数为垃圾值

在g++中默认初始化成员变量为0,而A *pa2 = new A(5)初始化成员变量为5

#include <iostream>
using namespace std;

class A
{
public:
    int m_num;

public:
    A(){};
    A(int num) : m_num(num)
    {
        cout << "construct" << endl;
    }
    ~A()
    {
        cout << "disconstruct" << endl;
    };
};

int main(int argc, char const *argv[])
{
    A *pa = new A;
    A *pa2 = new A();
    A *pa3 = new A(5);

    cout << "pa->m_num = " << pa->m_num << endl;
    cout << "pa2->m_num = " << pa2->m_num << endl;
    cout << "pa3->m_num = " << pa3->m_num << endl;

    delete pa;
    delete pa2;
    delete pa3;
    return 0;
}

运行结果:

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

new工作任务

调用operator new()--malloc

调用了分配对象的构造函数

delete工作任务

调用了分配对象的析构函数

调用operator delete()--free

new和delete 堆区空间操作(对比malloc和free)

new和delete操作基本类型的空间

new和malloc delete和free 没有区别

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

区别:

new 不用强制类型转换

new在申请空间的时候可以 初始化空间内容

new和delete操作基本类型的数组

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

new和delete操作类的空间

malloc不会调用构造函数 free不会调用析构函数

new 会调用构造函数 delete调用析构函数

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

new调用有参构造

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

new和delete操作对象数组

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

new内存分配细节探秘

  • new分配内存实际是调用malloc函数进行内存分配;
  • 思考:delete/free是如何知道要释放多大的内存?
    • 分配内存时,为了记录和管理分配出去的内存,额外多分配了不少内存,造成了浪费;尤其是你频繁的申请小块内存时,造成的浪费更明显,更严重
    • 实际分配情况

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

为什么要尽可能少的调用malloc?

  • 内存开销: 每次调用 malloc 都会引入额外的内存开销,包括内存分配表、堆管理等数据结构,这些开销可能会在大量小型分配时累积并消耗大量内存。
  • 内存泄漏风险: 使用 malloc 分配内存后,需要负责在不再使用内存时释放它。如果你频繁地调用 malloc,则需要管理和追踪许多不同的内存分配,容易出现内存泄漏问题,导致程序在运行时逐渐耗尽内存。
  • 性能开销: 内存分配和释放是相对较慢的操作,涉及到内部数据结构的维护、内存搜索等操作。频繁调用 malloc 可能会导致性能下降,特别是在大规模数据处理或高性能计算应用中。
  • 碎片化: 频繁分配和释放小块内存可能导致内存碎片化,即使系统总内存充足,也可能由于碎片化问题无法满足大块内存分配的需求。

为了减少 malloc 调用的次数,可以考虑以下方法:

  • 使用栈内存: 对于小型临时变量,可以使用栈内存而不是堆内存,因为栈内存的分配和释放非常快速。但要注意栈内存的生命周期通常较短。
  • 池化: 如果需要频繁创建和销毁对象,可以使用内存池技术,通过一次性分配一大块内存并自行管理对象的分配和释放。
  • 缓存: 对于某些可复用对象,可以使用缓存来避免频繁分配和释放内存。这在对象池等场景中很有用。
  • 避免不必要的动态分配: 如果可以在编译时确定数组或数据结构的大小,可以使用栈数组或静态分配来避免动态分配。

new和delete的重载

为什么要重载 new

监测内存创建销毁,统计和监控泄漏

在C++中,内存管理是开发者的一项重要责任,也是容易出错的地方。开发者可能会遗忘释放已分配的内存,导致内存泄漏。重载new和delete可以帮助开发者更好地追踪和管理内存分配。通过在重载的new和delete操作符中插入日志或者调试语句,开发者可以监测和记录所有内存分配和释放的情况,从而检测内存泄漏。

例如,以下的代码展示了如何重载new和delete操作符来监测和追踪内存分配:

void* operator new(size_t size) {
    void* p = malloc(size);
    std::cout << "Allocated " << size << " bytes at address " << p << std::endl;
    return p;
}

void operator delete(void* p) {
    std::cout << "Deallocated memory at address " << p << std::endl;
    free(p);
}

内存对齐的处理

在一些硬件平台和操作系统上,为了实现最优性能,数据需要按照某种特定的边界对齐。如果没有对齐,可能会导致性能下降,甚至运行错误。通过重载new和delete,我们可以为特定的类实现定制的内存对齐方式。

下面的代码演示了如何重载new和delete操作符来实现内存对齐:

class Aligned {
public:
    static void* operator new(std::size_t size) {
        void* p = std::aligned_alloc(alignof(Aligned), size);
        if (!p) {
            throw std::bad_alloc();
        }
        return p;
    }

    static void operator delete(void* p) {
        std::free(p);
    }
};

特定应用:多进程内存共享

在某些情况下,多个进程可能需要访问同一块内存区域。在这种情况下,可以通过重载new和delete操作符,实现在共享内存区域中分配和释放对象。

例如,以下的代码展示了如何通过重载new和delete来在共享内存中分配和释放对象:

// 假设SharedMemoryManager是一个用于管理共享内存的类
class SharedMemoryManager {
public:
    void* allocate(size_t size);
    void deallocate(void* p);
};

class SharedMemoryObject {
public:
    void* operator new(size_t size) {
        return SharedMemoryManager::allocate(size);
    }

    void operator delete(void* p) {
        SharedMemoryManager::deallocate(p);
    }
};

在以上的例子中,SharedMemoryObject类的对象将会被分配在共享内存中,从而可以被多个进程访问。

重载全局的 new 和 delete

全局的new和delete操作符可被重载以满足特定的需求,比如定制内存管理策略,或者为内存分配和释放添加自定义行为。要注意,这些全局重载将影响到整个程序的范围,包括标准库的容器等,所以在实践中应谨慎使用。

void* operator new(size_t size) {
    // ... 实现代码
}

void operator delete(void* p) {
    // ... 实现代码
}

operator new需要返回一个足够大,可以容纳请求内存大小的指针。如果内存分配失败,需要抛出std::bad_alloc异常。operator delete需要释放传入的指针指向的内存。

在全局new和delete中添加定制行为

下面的代码将在全局的new和delete操作符中添加一些定制的行为。在分配和释放内存时,我们会打印一些信息到控制台,以便于跟踪内存的使用情况:

#include <cstdlib>
#include <iostream>

void* operator new(size_t size) {
    void* p = std::malloc(size);
    if (!p) {
        throw std::bad_alloc();
    }

    std::cout << "Allocated " << size << " bytes at address " << p << std::endl;
    return p;
}

void operator delete(void* p) {
    std::cout << "Deallocated memory at address " << p << std::endl;
    std::free(p);
}

以上代码演示了如何在全局的new和delete操作符中添加自定义的行为。这种方式在实际开发中可以帮助我们更好地理解和跟踪内存的使用情况。不过请注意,添加过多的日志可能会对性能产生影响,所以在生产环境中通常不会添加过多的日志信息。

重载类的操作符 new 和 delete

对类的new和delete操作符进行重载允许我们为该类的对象提供定制的内存管理策略。这对于需要进行特殊内存管理的类来说特别有用,例如需要在共享内存中创建的对象,或者需要进行特殊对齐的对象。

类new和delete操作符重载基础

对类的new和delete操作符进行重载的基本形式如下:

class MyClass {
public:
    static void* operator new(std::size_t size);
    static void operator delete(void* p);
};

operator new需要返回一个足够大,可以容纳请求内存大小的指针。如果内存分配失败,需要抛出std::bad_alloc异常。operator delete需要释放传入的指针指向的内存。

对齐的内存分配

假设我们有一个需要8字节对齐的类,我们可以通过重载new和delete操作符来满足这个要求:

#include <cstdlib>
#include <new>

class Aligned {
public:
    static void* operator new(std::size_t size) {
        void* p = std::aligned_alloc(8, size);
        if (!p) {
            throw std::bad_alloc();
        }
        return p;
    }

    static void operator delete(void* p) {
        std::free(p);
    }
};

在这个例子中,我们使用了std::aligned_alloc函数来进行对齐的内存分配。如果分配失败,我们抛出std::bad_alloc异常。在operator delete中,我们简单地调用std::free来释放内存。

共享内存的分配

假设我们有一个需要在共享内存中创建的对象,我们可以通过重载new和delete操作符来实现:

// 假设SharedMemoryManager是一个用于管理共享内存的类
class SharedMemoryManager {
public:
    static void* allocate(std::size_t size);
    static void deallocate(void* p);
};

class SharedMemoryObject {
public:
    static void* operator new(std::size_t size) {
        return SharedMemoryManager::allocate(size);
    }

    static void operator delete(void* p) {
        SharedMemoryManager::deallocate(p);
    }
};

在这个例子中,SharedMemoryObject类的对象将会在共享内存中创建和销毁。这允许我们在多个进程间共享这些对象。

定位new(placement new)

放置new (placement new) 是一个特殊版本的new操作符,它允许程序员将对象创建在已经分配的内存上。换句话说,它允许我们"放置"一个新的对象在我们指定的、已经存在的内存位置上。

功能

在已经分配的原始内存中初始化一个对象;

  • 已经分配,定位new并不分配内存,你需要提前将这个定位new要使用的内存分配出来
  • 初始化一个对象(初始化一个对象的内存),调用这个对象的构造函数不再分配内存;

使用placement new

在普通的new操作中,首先会申请一块足够的内存,然后在这块内存上构造对象。但是在placement new中,内存必须已经存在,它只负责在指定的内存上构造对象。以下是一个使用placement new的例子:

#include <new> // 需要包含这个头文件来使用placement new

char buffer[1024]; // 预分配的内存

int* p = new (buffer) int(123); // 在buffer上放置一个int对象

对于类,placement new最好在我们需要使用的类中重载,否则在类外重载会影响到其它类型分配空间

#include <iostream>
using namespace std;

void *operator new(size_t size)
{
    void *p = malloc(size);
    std::cout << "Allocated " << size << " bytes at address " << p << std::endl;
    return p;
}

void operator delete(void *p)
{
    std::cout << "Deallocated memory at address " << p << std::endl;
    free(p);
}

class A
{
public:
    int m_num;

public:
    A(){cout<<"default construct"<<endl;};
    A(int num) : m_num(num)
    {
        cout << "construct" << endl;
    }
    ~A()
    {
        cout << "disconstruct" << endl;
    };
    void *operator new(size_t size, void *p)
    {
        cout << "placement new" << endl;
        return p;
    }
};

int main(int argc, char const *argv[])
{
    void *p = (void *)new char[sizeof(A)];
    A *pa = new (p) A();

    pa->m_num = 5;
    cout << *((int *)p) << endl;
    delete pa;
    return 0;
}

C++内存管理(2)new、delete详解,C/C++内存管理精讲,c++,开发语言,嵌入式,内存管理

placement new对象的销毁

由于placement new仅仅在已经分配的内存上创建对象,而不会分配内存,所以当不再需要这个对象时,我们需要手动调用该对象的析构函数来销毁对象:

p->~int(); // 手动调用析构函数

需要注意的是,这里只销毁了对象,但并没有释放内存。内存的释放需要根据实际的情况来处理。例如,如果这块内存是在栈上分配的,那么当退出作用域时会自动释放;如果是在堆上分配的,那么可能需要手动释放。

placement new的应用

placement new的一个主要应用是当我们需要在特定的位置创建对象时,比如在已分配的堆内存上,或者在栈内存上,甚至在硬件指定的特定内存地址上。

此外,placement new也常用于实现自定义的内存池,内存池可以减少动态分配和释放内存带来的开销。我们可以预先分配一大块内存,然后通过placement new在这块内存上创建对象,从而提高内存使用的效率。

硬件编程

如果知道了硬件设备的地址,想要将那个硬件设备与一个C++类直接关联,那么定位new就非常有效了

通过将placement new可以将C++的类之间关联到硬件设备上,操作该对象就相当于操作硬件

如下面程序所示,假如操作STM32的GPIOB->GPIO_Pin1,假设GPIO_Pin1的存储器映射地址为0x00005600。由于类A的对象pa的地址就是对象pa内首个字段m_num的地址,因此操作m_num就相当于操作地址0x00005600。

#include <iostream>
using namespace std;

class A
{
public:
    int m_num;

public:
    A(){};
    A(int num) : m_num(num)
    {
        cout << "construct" << endl;
    }
    ~A()
    {
        cout << "disconstruct" << endl;
    };
};

int main(int argc, char const *argv[])
{
    //访问硬件:将C++的类之间关联到硬件设备上,操作该对象就相当于操作硬件
    //单片机/STM32/ARM9:操作硬件的物理地址就相等于操作该硬件
    //GPIOB->GPIO_Pin1
    void *p = (void*)0x00005600;
    A *pa = new(p) A();
    pa->m_num = 1;//拉高电平
    pa->m_num = 0;//拉低电平

    return 0;
}

实现基础库

基础库一般为了效率要先预分配内存,然后在预分配的内存上执行构造,几乎所有的C++容器都用到了定位new

多种版本的operator new重载

优先级:内部new、全局new

可以重载很多版本的operator new,只要每个版本参数不同就行,但是第一个参数是固定的,都是size_t,表示要new对象的sizeof值文章来源地址https://www.toymoban.com/news/detail-697829.html

到了这里,关于C++内存管理(2)new、delete详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++内存管理(new和delete)

    目录 1. new/delete操作内置类型 2. new和delete操作自定义类型 3. operator new与operator delete函数  4 .new和delete的实现原理 1 .内置类型 2 .自定义类型 new的原理 delete的原理 new T[N]的原理 delete[]的原理 5. 定位new表达式(placement-new) 6. malloc/free和new/delete的区别 7.内存泄漏 内存泄漏分类 8.如

    2024年02月02日
    浏览(38)
  • C++如何进行内存管理 (new、delete)

    我成功是因为我有决心,从不踌躇。——拿破仑  本章是关于c++内存管理的文章,字数不多,内容简单,希望对你有所帮助!! 话不多说安全带系好,发车啦 (建议电脑观看) 。 附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或

    2024年02月08日
    浏览(100)
  • 【C++破局】C++内存管理之new与deleted剖析

    📚lovewold少个r博客主页     ⚠️ 本文重点 : c++内存管理部分知识点梳理 👉 【C-C++入门系列专栏】 : 博客文章专栏传送门 😄 每日一言 :花有重开日,人无再少年! C/C++的内存分配机制 内存分区 1. 内核空间(Kernel Space): 2. 栈空间(Stack): 3. 内存映射段(Memory Mapp

    2024年02月05日
    浏览(41)
  • 【C++】C/C++内存管理-new、delete

    ヾ(๑╹◡╹)ノ\\\" 人总要为过去的懒惰而付出代价 ヾ(๑╹◡╹)ノ\\\" 代码展示: 栈区 :局部变量 sizeof(数组名):整个数组的大小 sizeof(字符串):注意‘\\0’ 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。 内存映射段是高效的I/O映射方式,用于装载

    2024年02月11日
    浏览(45)
  • 【C++初阶】七、内存管理(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】六、类和对象(初始化列表、static成员、友元、内部类)-CSDN博客  ==================

    2024年02月05日
    浏览(44)
  • 【C++入门】new和delete(C/C++内存管理)

    1.C/C++内存分布 我们先来看一个小问题: 问题: 下面我们就来简单分析一下: 首先 globalVar 是一个全局变量,其次 staticGlobalVar 是一个静态全局变量, staticVar 是静态局部变量,都在静态区(数据段)。最后 localVar 是个局部变量, num1 是个整型数组,那它们是在栈上的。 ch

    2024年02月11日
    浏览(40)
  • 【C++干货基地】深度理解C++中的高效内存管理方式 new & delete

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 : 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活!   哈喽各位铁汁们好啊,我是博主鸽芷咕《C++干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的城市有没有这种实惠又全面的零食基地呢?C++ 本身作

    2024年04月26日
    浏览(40)
  • 【C++庖丁解牛】C++内存管理 | new和delete的使用以及使用原理

    📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 我们先来看下面的一段代码和相关问题 选择题: 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

    2024年03月09日
    浏览(55)
  • C/C++内存管理(含C++中new和delete的使用)

    我们先来看下面的一段代码和相关问题。 说明 : 栈 又叫堆栈– 非静态局部变量/函数参数/返回值 等等,栈是向下增长的。 内存映射段 是高效的 I/O 映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。( Linux 课程如果没学到

    2024年02月05日
    浏览(43)
  • 【C++】深入探讨内存管理:malloc/free与new/delete的区别以及如何避免内存泄漏

    在软件开发中,正确处理内存管理是至关重要的一环。在C++编程中,我们经常会用到动态内存管理的工具,比如 malloc/free 和 new/delete 。本文将深入探讨 malloc/free 与 new/delete 之间的区别,以及如何有效地避免内存泄漏问题。 都是用于从堆上申请空间,并需要手动释放。 mallo

    2024年02月22日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包