tips
- 其实进程运行起来或者说程序运行起来都是去执行函数,任务就是不断的去执行函数。C++的入口就是main函数,然后在这个函数当中可能碰到程序某些调用其他函数的语句就去调用其他函数。在全局的区域可以去创建变量,定义函数,但就是不可以去具体的执行某个语句(比如说想要在全区去调用函数)那是不可能的,因为根本就执行不到那边,这是一个原则性的错误。
- cin,cout能够自动识别类型的内幕,主要就是说在运算符重载的时候用了函数重载(这个内置类型给他分类分开来),所以说看起来好像能够自动识别类型。
- 如果说想要用cout去打印指针int*的话,那么打印出来的就是一个地址。
- 如果说想要用cout去直接打印指针char的话,那么这时候他会给你打印出一个字符串来,他碰到\0才会停下打印。如果说去打印这个char指针的解引用的话,打印出来的就是一个字符。
new/delete出现的原因
- 在c当中,我们如果需要向内存的堆区去动态申请开辟空间的话,我们一般用的配套选择都是malloc与free。
- 但在实际过程当中,malloc与free还是会有很多缺陷与不方便的地方。所以说c++发明出了new与delete。
- 我们首先来谈一谈内置类型,对于那些内置类型的变量与数组的内存动态申请资源的话,其实两者之间的差距并没有特别的明显,可以说基本上没有任何区别。
- 但是当这个类型涉及到自定义类型的时候,比如说我们需要为一个自定义类型的变量或者说自定义类型变量数组在内存的堆区上面去动态申请内存资源的话,仅仅用malloc与free不仅只能单单的申请出这么一块空间出来,而不能够及时的进行初始化,并且等到程序运行结束的时候对于这个自定义类型变量当中可能需要进行资源的清理,free的话是直接把那块申请出来的内存堆区去空间给释放掉,他并不能够达到我们的资源清理的一个效果。所以说对于自定义类型的变量也好,数组也好,malloc与free都不太行了,这就是new与delete出现的原因
new的用法详解
delete的用法详解
- 其实就是跟new配套着去使用,如果说申请的是一个变量的空间,那么就直接delete指针;
delete ptr1;
- 如果说是申请了一个数组的堆区空间,那么就直接delete[ ] 指针
delete[] ptr2;
new底层原理与operator new库函数
我们现在讨论的是针对于自定义类型变量(举个例子嘛)
- 我们再来比较一下malloc与new,你会发现c语言当中的malloc它只会负责去开空间,而对于new来说,他不仅会帮你把内存堆去的空间开好,(因为你这个内存堆取的空间实际上相当于就是一个实例化对象),所以说他还会去调用构造函数。这个就是new相对于malloc的独特优秀之处。
- 但无论怎么样,这个new去完成任务的时候首先也还是得去向内存地区开一块空间,那malloc刚好就是负责去向内存堆区开辟空间的,有现成的,我为什么不去用呢?所以这里new的部分任务(开空间)就是去用现成的malloc。
- 但是又不能简简单单的完全用malloc去完成new的前部分任务(开空间),因为面向对象的语言都有一个特点:就是当处理失败的时候,他不喜欢像c语言那样去返回值:-1, NULL…他喜欢的是抛异常(这个异常是需要去捕获的),所以说首先在malloc开空间的基础上再添加一些抛异常的机制,这就完成了new的前部分任务(开辟堆区空间),然后把这个过程给他封装成一个函数,就是operator new(这个不是直接的运算符重载,它是一个全局函数,是在库里面的一个函数),所以说现在我们就正式知道了,new开空间其实就是去调用函数operator new。
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
汇编验证:
delete底层原理与operator delete库函数
- 这个delete操作符实际上它的任务也分为两个部分。
- 首先必须先要去执行这个自定义类型实例化对象的析构函数,将动态申请的资源该释放的先释放掉,免得造成内存泄露。
- 然后接下来再去用free去释放一下之前用new申请的一个堆区空间。在这个过程当中封装成了一个函数,也是一个库函数:operator delete。这个operator delete函数如果你去看一下它函数的源码的话,你会发现它里面也在执行free罢了。
//operator delete 源码
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse ); /// ATTENSION!!!
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
- 这边补充一下:从库里面的源码发现,C语言当中的free他实际上是个宏函数:
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
汇编验证:
举例子说明operator new,构造函数,析构函数,operator delete 四者顺序关系
malloc/free与new/delete区别与相关总结
定位new显示调用构造函数
- 假设现在我为一个实例化对象在内存的堆区上面去申请开辟了一块空间。但是我就是仅仅开辟了一块空间,我没有去调用构造函数,对于这个属于实例化对象的内存空间进行初始化。这也从侧面反映我此时此刻开辟内存堆区空间并不使用new
- 然后此刻的状况就是单纯的开辟了一块内存堆区空间,并且这个内存堆区的空间是属于一个实例化对象。
- 此时此刻好比实例化对象已经创建出来了,按道理来说应该是要一语双关去隐式调用构造函数进行一个初始化操作,但现在由于情况特殊,完不成。因为我是向内存堆区申请一块实例化对象的内存空间,而不是像以往那样在栈上创建一个实例化对象。
- 这时候我可以用定位new去显示调用构造函数。语法格式如下:
- 值得注意的是,但我需要去释放这块实例化对象的堆区空间的时候,由于可能这个实例化对象动态申请了资源,因此先需要去调用析构函数,这时候这个析构函数也是需要我手动去显示调用。
- 实例模拟:
#include <iostream>
#include <stdlib.h>
using namespace std;
class Stack
{
public:
Stack(int capacity)
:_size(0)
,_capacity(capacity)
{
_p = (int*)malloc(sizeof(int) * _capacity);
if (_p == nullptr)
{
perror("malloc failed");
return;
}
}
~Stack()
{
free(_p);
}
private:
int _size;
int _capacity;
int* _p;
};
int main()
{
Stack* ps = (Stack*)malloc(sizeof(Stack));
if (ps == nullptr)
{
perror("malloc failed");
return 0;
}
new(ps)Stack(10);
ps->~Stack();
return 0;
}
内存泄漏
一个进程正常结束之后,这些动态申请的内存资源就算你没有释放的话,操作系统也会自己给他解掉,所以说我们平时写的程序的话,内存泄露其实影响不太大;真正要命的就是在那些服务程序里面,或者说游戏程序里面,因为他那个程序是24×7不停机的,长期的内存泄露下去的话,将会产生各种各样的问题。