目录
1. C/C++
内存分布
2. C
语言中动态内存管理方式
3. C++
中动态内存管理
4. operator new
与
operator delete
函数
5. new
和
delete
的实现原理
6.
定位
new
表达式
(placement-new)
1. C/C++
内存分布
【说明】
1.
栈
又叫堆栈
--
非静态局部变量
/
函数参数
/
返回值等等,栈是向下增长的。
2.
内存映射段
是高效的
I/O
映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。(
Linux
课程如果没学到这块,现在只需要了解一下)
3.
堆
用于程序运行时动态内存分配,堆是可以上增长的。
4.
数据段
--
存储全局数据和静态数据。
5.
代码段
--
可执行的代码
/
只读常量。
2. C
语言中动态内存管理方式:
malloc/calloc/realloc/free
malloc
的实现原理?
3. C++
内存管理方式
C
语言内存管理方式在
C++
中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因
此
C++
又提出了自己的内存管理方式:
通过
new
和
delete
操作符进行动态内存管理
。
3.1 new/delete
操作内置类型
注意:申请和释放单个元素的空间,使用
new
和
delete
操作符,申请和释放连续的空间,使用
new[]
和
delete[]
,注意:匹配起来使用。
3.2 new
和
delete
操作自定义类型
/*
operator new
:该函数实际通过
malloc
来申请空间,当
malloc
申请空间成功时直接返回;申请空间
失败,尝试执行空
间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
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
);
}
/*
operator delete:
该函数最终是通过
free
来释放空间的
*/
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
);
__FINALLY
_munlock
(
_HEAP_LOCK
);
/* release other threads */
__END_TRY_FINALLY
return
;
}
/*
free
的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道,
operator new
实际也是通过
malloc
来申请空间
,如果
malloc
申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
就继续申请,否则就抛异常。
operator delete
最终是通过
free
来释放空间的
。
4.2
重载
operator new
与
operator delete(
了解
)
注意:一般情况下不需要对
operator new
和
operator delete
进行重载,除非在申请和释放空间
时候有某些特殊的需求。比如:在使用
new
和
delete
申请和释放空间时,打印一些日志信息,可
以简单帮助用户来检测是否存在内存泄漏。
//
重载
operator delete
,在申请空间时:打印在哪个文件、哪个函数、第多少行,申请了多少个
字节
void*
operator new
(
size_t
size
,
const
char*
fileName
,
const
char*
funcName
,
size_t
lineNo
)
{
void*
p
=
::
operator new
(
size
);
cout
<<
fileName
<<
"-"
<<
funcName
<<
"-"
<<
lineNo
<<
"-"
<<
p
<<
"-"
<<
size
<<
endl
;
return
p
;
}
//
重载
operator delete
,在释放空间时:打印再那个文件、哪个函数、第多少行释放
void
operator delete
(
void*
p
,
const
char*
fileName
,
const
char*
funcName
,
size_t
lineNo
)
{
cout
<<
fileName
<<
"-"
<<
funcName
<<
"-"
<<
lineNo
<<
"-"
<<
p
<<
endl
;
::
operator delete
(
p
);
}
int
main
()
{
//
对重载的
operator new
和
operator delete
进行调用
int*
p
=
new
(
__FILE__
,
__FUNCTION__
,
__LINE__
)
int
;
operator delete
(
p
,
__FILE__
,
__FUNCTION__
,
__LINE__
);
return
0
;
}
//
上述调用显然太麻烦了,可以使用宏对调用进行简化
//
只有在
Debug
方式下,才调用用户重载的
operator new
和
operator delete
#ifdef _DEBUG
#define new new(__FILE__, __FUNCTION__, __LINE__)
#define delete(p) operator delete(p, __FILE__, __FUNCTION__, __LINE__)
#endif
int
main
()
{
int*
p
=
new
int
;
delete
(
p
);
return
0
;
}
5. new
和
delete
的实现原理
5.1
内置类型
如果申请的是内置类型的空间,
new
和
malloc
,
delete
和
free
基本类似,不同的地方是:
new/delete
申请和释放的是单个元素的空间,
new[]
和
delete[]
申请的是连续空间,而且
new
在申
请空间失败时会抛异常,
malloc
会返回
NULL
。
5.2
自定义类型
new
的原理
1.
调用
operator new
函数申请空间
2.
在申请的空间上执行构造函数,完成对象的构造
delete
的原理
1.
在空间上执行析构函数,完成对象中资源的清理工作
2.
调用
operator delete
函数释放对象的空间
new T[N]
的原理
1.
调用
operator new[]
函数,在
operator new[]
中实际调用
operator new
函数完成
N
个对
象空间的申请
2.
在申请的空间上执行
N
次构造函数
delete[]
的原理
1.
在释放的对象空间上执行
N
次析构函数,完成
N
个对象中资源的清理
2.
调用
operator delete[]
释放空间,实际在
operator delete[]
中调用
operator delete
来释
放空间
6.
定位
new
表达式
(placement-new)
(了解)
定位
new
表达式是在
已分配的原始内存空间中调用构造函数初始化一个对象
。
使用格式:
new (place_address) type
或者
new (place_address) type(initializer-list)
place_address
必须是一个指针,
initializer-list
是类型的初始化列表
使用场景:
定位
new
表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如
果是自定义类型的对象,需要使用
new
的定义表达式进行显示调构造函数进行初始化。
class
A
{
public
:
A
(
int
a
=
0
)
:
_a
(
a
)
{
cout
<<
"A():"
<<
this
<<
endl
;
}
~A
()
{
cout
<<
"~A():"
<<
this
<<
endl
;
}
private
:
int
_a
;
};
//
定位
new/replacement new
int
main
()
{
// p1
现在指向的只不过是与
A
对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
有执行
A
*
p1
=
(
A
*
)
malloc
(
sizeof
(
A
));
new
(
p1
)
A
;
//
注意:如果
A
类的构造函数有参数时,此处需要传参
p1
->
~A
();
free
(
p1
);
A
*
p2
=
(
A
*
)
operator new
(
sizeof
(
A
));
new
(
p2
)
A
(
10
);
p2
->
~A
();
operator delete
(
p2
);
return
0
;
}