malloc free使用规范
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。
C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
new:
A* a = new A
有人也称new为new operator,区别于下面的operator new
这里分为三步:
- 分配内存
- 调用A()构造对象
- 返回分配指针。
事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),
否则调用全局::operator new(size_t ),后者由C++默认提供。因此前面的步骤也就是:
- 调用operator new (sizeof(A))
- 调用A:A()
- 返回指针
/**
* ::operator new()源代码
* 主要就是调用一个malloc,若调用不成功,调用_callnewh
*/
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
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
delete():
- 调用一个对象的析构函数,
- 调用operator delete
/**
* 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 );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
new[]作用:
调用operator new分配空间。调用N次构造函数分别初始化每个对象。
/**
* new[]源代码
*
*/
void *__CRTDECL operator new[](size_t count) _THROW1(std::bad_alloc)
{ // try to allocate count bytes for an array
return (operator new(count));
}
delete[]作用:
调用N次析构函数清理对象,调用operator delete释放空间。
注意内存泄漏
/**
* delete[]源代码
*
*/
void operator delete[]( void * p )
{
RTCCALLBACK(_RTC_Free_hook, (p, 0))
operator delete(p);
}
placement new
Ctor&Dtor:构造函数不能被直接调用,析构函数可以被直接调用,想要直接调用构造函数要用placement new
A *p=new (mem) A
所谓placement new就是在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。
没有所谓的placement delete,因为placement new根本没有分配memory
或者称呼与placement new对应的operator delete为placement delete
placement new的好处:
1)在已分配好的内存上进行对象的构建,构建速度快。
2)已分配好的内存可以反复利用,有效的避免内存碎片问题。
/**
* placement new实例
*
*/
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A's constructor" << endl;
}
~A()
{
cout << "A's destructor" << endl;
}
void show()
{
cout << "num:" << num << endl;
}
private:
int num;
};
int main()
{
char mem[100];
mem[0] = 'A';
mem[1] = ' ';
mem[2] = ' ';
mem[3] = ' ';
cout << (void*)mem << endl;
A* p = new (mem)A;
cout << p << endl;
p->show();
p->~A();
getchar();
}
(1)用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。如本例就是在栈上生成一个对象。
(2)使用语句A* p=new (mem) A;定位生成对象时,指针p和数组名mem指向同一片存储区。所以,与其说定位放置new操作是申请空间,还不如说是利用已经请好的空间,真正的申请空间的工作是在此之前完成的。
(3)使用语句A *p=new (mem) A;定位生成对象时,会自动调用类A的构造函数,但是由于对象的空间不会自动释放(对象实际上是借用别人的空间),所以必须显示的调用类的析构函数,如本例中的p->~A()。
(4)如果有这样一个场景,我们需要大量的申请一块类似的内存空间,然后又释放掉,比如在在一个server中对于客户端的请求,每个客户端的每一次上行数据我们都需要为此申请一块内存,当我们处理完请求给客户端下行回复时释放掉该内存,表面上看者符合c++的内存管理要求,没有什么错误,但是仔细想想很不合理,为什么我们每个请求都要重新申请一块内存呢,要知道每一次内从的申请,系统都要在内存中找到一块合适大小的连续的内存空间,这个过程是很慢的(相对而言),极端情况下,如果当前系统中有大量的内存碎片,并且我们申请的空间很大,甚至有可能失败。为什么我们不能共用一块我们事先准备好的内存呢?可以的,我们可以使用placement new来构造对象,那么就会在我们指定的内存空间中构造对象。
嵌入式指针
请使用 1 == p
而不是 p == 1
对于单纯常量,最好以const对象或enums替换#defines。
对于形似函数的宏(macros),最好改用inline函数替换#defines。
尽可能使用const:
const语法虽然变化多端,但并不莫测高深。
如果关键字const出现在星号左边,表示被指物是常量;
如果出现在星号右边,表示指针自身是常量;
如果出现在星号两边,表示被指物和指针两者都是常量。
令operator=返回一个reference to *this
https://blog.csdn.net/nodeathphoenix/article/details/38146421
宁以pass-by-reference-to-const替换pass-by-value