【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++模板初阶讲解 🔍

相关文章
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
50 13
|
29天前
|
存储 程序员 编译器
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
|
2月前
|
缓存 监控 算法
Python内存管理:掌握对象的生命周期与垃圾回收机制####
本文深入探讨了Python中的内存管理机制,特别是对象的生命周期和垃圾回收过程。通过理解引用计数、标记-清除及分代收集等核心概念,帮助开发者优化程序性能,避免内存泄漏。 ####
63 3
|
3月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
155 5
|
3月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
81 3
|
3月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
163 4
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
50 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
39 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4

热门文章

最新文章