【C++基础(九)】C++内存管理--new一个对象出来

简介: 【C++基础(九)】C++内存管理--new一个对象出来

1. 前言

在C语言中,有四个内存管理函数:

malloc,calloc,realloc和free

但是它们的使用十分的不方便:

int* p=(int*)malloc(sizeof(int)*n);

代码量很大,并且有一个新的问题:

malloc函数不会初始化变量
当变量是自定义类型的时候
C语言的内存管理函数无法
自动调用变量的构造/析构函数!

于是C++祖师爷发话了:
C++要自己搞一个内存管理!


2. new

2.1 new的使用方法

new的使用方法:

  1. 开辟多个空间
int* p = new int[100];

这段代码可以这样理解:


  1. 开辟一个空间并初始化
class A
{
public:
 A(int a = 0)
 : _a(a)
 {}
private:
 int _a;
};
A* p = new A(10);

使用小括号()
这段代码初始化了一个对象成10
并且将地址赋值给指针变量p


  1. 开辟多个空间并初始化
A* p = new A[5]{1,2,3};

这段代码可以这样理解:


2.2 new的特性(对比malloc)

new的特性:

  • 对于内置类型来说,new和malloc
    并没有太大的区别

  • 对于自定义类型来说,new会调用
    自定义类型的构造函数,而malloc不会

  • new可以初始化变量的内容

3. delete

delete对比C语言中的free
是用来使用同台开辟的空间的/1

3.1 delete的使用方法

delete的使用:

  1. 直接使用delete
int* p = new int(10);
//使用指针p
delete p;

当new堆区空间时只开辟了一份空间
释放空间时直接使用delete即可

  1. 使用delete[]
int* p = new int[10]{1,2,3,4};
//使用指针p
delete[] p;

当new使用方括号[]开辟多个空间时
delete也要对应的加上[]来释放空间
否则会出错


3.2 delete的特性(对比free)

delete特性:

  • 对于内置类型,delete和free没有区别
  • 对于自定义类型,delete会调用
    自定义类型的析构函数,而free不会

  • delete面对不同的情况需要加上[ ]
    然而free不用考虑

对于自定义类型来说
有可能类中有指针指向一块堆区
如果不调用析构函数释放这块空间
直接用free释放掉对象的空间
那么这块空间就会内存泄漏!


4. 全局函数operator new

不同于C语言的malloc和free函数
new和delete是两个操作符
那new是怎样开辟空间的呢?

new在底层调用了operator new

注:
operator new是全局函数
不是运算符重载

库中的operator new函数:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
void *p;
while ((p = malloc(size)) == 0)
  if (_callnewh(size) == 0)
  {
      // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
      static const std::bad_alloc nomem;
      _RAISE(nomem);
  }
return (p);
}

可以发现,new的底层实际上是
使用了malloc来实现开辟空间

开辟空间成功,会返回指向此空间的指针
开辟空间失败,会抛异常

抛异常不是本节课的重点
它是C++一个全新的知识
在后面的章节会详细介绍
一般情况下,new申请空间都不会失败!


5. 全局函数operator delete

和new对应的delete的底层
也是调用了operator delete来
释放堆区开辟的空间

库中的operator delete函数:

#define free(p) _free_dbg(p, _NORMAL_BLOCK)
void operator delete(void *pUserData)
{
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);
     __TRY
         pHead = pHdr(pUserData);
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);
     __END_TRY_FINALLY
     return;
}

看不懂没关系,只需要抓住重点:
delete里面调用了:
_free_dbg(p, _NORMAL_BLOCK)
然而此函数正是free函数定义的宏替换!

说明delete的底层实现实际上
是调用了C语言中的free函数

delete是使用free来释放空间的


6. new的实现原理

对于内置类型来说,new和
malloc的实现是一样的

对于自定义类型来说

  • new的原理步骤
  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数
    完成对象的构造
  • new T[N]的原理步骤
  1. 调用operator new[]函数
    完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数

7. delete的实现原理

对于内置类型来说,delete
和free的实现是一样的

对于自定义类型来说

  • delete的原理步骤
  1. 在空间上执行析构函数
    完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间
  • delete[]的原理步骤
  1. 在释放的对象空间上执行N次析构函数
    完成N个对象中资源的清理
  2. 调用operator delete[]释放空间

8. 总结以及拓展

此章总结完,就可以去new一个对象了
并且C++和C语言中的内存管理区别
是面试中的常考点,和指针与引用的区别
俗称"面试山上的看门二虎"

下面就来总结一下:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void, 在使用时必须强转,new不需要,因为new后跟的是空间的类型*
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

拓展:内存泄漏

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费

更多关于内存泄漏的内容:
内存泄漏介绍
内存泄漏原因
内存泄漏检测方法
内存泄漏检测工具

可以参考这两篇文章:


🔎 下期预告:C++模板初阶讲解 🔍

相关文章
|
3月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
129 26
|
4月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
62 1
|
7月前
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
7月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
7月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
7月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
3月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
833 0
|
3月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
238 0
|
3月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
230 0

热门文章

最新文章