手工管理内存 heap资源
软件使用内存的行为特征---手动定/修改分配和归还内存的工作。
C++内存管理例程的行为?
operator new, operator delete, +new-handler, operator new[], operator delete[]
注:不负责STL容器的HEAP管理
多线程环境下的内存管理?
heap——可被改动的全局性资源。race condition(竞速状态)出现 大家都同时访问一个资源,只拼速度,不稳定。
可改动的static数据。thread-aware(线程感知)程序员 同步控制(synchronization),无锁算法 ,精心防止并发访问(concurrent access)?
49*了解new-handle的行为
operator new无法满足某一种内存分配需求时,会怎么做呢?
1、调用一个客户指定的错误处理函数——一个new-handler。
2、抛出异常bad_alloc(【旧】返回一个null指针)。
客户如何指定的?
#ifdef _M_CEE_PURE
typedef void (__clrcall * new_handler) ();
#else
typedef void (__cdecl * new_handler) ();//new_handler是没有参数也不返回任何东西的函数指针
#endif
_CRTIMP2 new_handler __cdecl set_new_handler(_In_opt_ new_handler) _THROW0(); // establish alternate new handler//获得一个new_handler并返回一个new_handler的函数。参数指向operater new无法分配足够内存时应该调用的new-handler函数。返回被替换的那个new-handler函数。
_STD_END <new>头文件
客户定义的错误处理函数new handler应该什么样子?
举例:
void outOfMen()//没有参数也不返回任何值的函数
{
std::cerr<<"Unable to satisfy request for memory\n";//提示
//1让更多内存可用——程序开始执行就分配一块内存,当new-handler第一次被调用,将它们释还给程序使用。
//2调用std::set_new_handler呼叫另一个new_handler函数。或者直接修改自身,比如改static数据,全局数据,namespace数据。
//3抛出bad_alloc异常(或派生),异常不会被operatornew捕捉,会被传播到内存索求处。
//4不返回,调用abort或者exit终止程序。
std::abort();//终止程序
}
int main()
{
std::set_new_handler(outOfMen);//登录处理函数
int* pBigDataArray = new int[1000000000L];
return 0;
}
第二个话题:实现类自己的new_handler:new类对象的时候,出问题时,调用本类自己的new_handler。
技术思路:Widget类的operator new操作
1、 把Widget的处理函数登录
2、 把 登录返回值——旧的处理函数,存起来(资源方式)
3、 分配内存
4、 不论正确与否,结束operator new操作之前,(资源类析构函数完成)把旧的处理函数登录,恢复全局之前的状态。
//*******.h********
#include <iostream>
class Widget{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
class NewHandlerHolder//2 把登录返回值——旧的处理函数,存起来(资源方式)
{
public:
explicit NewHandlerHolder(std::new_handler nh ):handler(nh){}
~NewHandlerHolder(){std::set_new_handler(handler);}//4 不论正确与否,结束operator new操作之前,(资源类析构函数完成)把旧的处理函数登录,恢复全局之前的状态。
private:
std::new_handler handler; //记录handler
NewHandlerHolder(const NewHandlerHolder&); //阻止copying
NewHandlerHolder operator=(const NewHandlerHolder&);
};
//*******.cpp********
#include "template1.h"
std::new_handler Widget::currentHandler = 0;
std::new_handler Widget::set_new_handler( std::new_handler p ) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;//1把Widget的处理函数登录
return oldHandler;
}
void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));//1把Widget的处理函数登录//2 把登录返回值——旧的处理函数,存起来(资源方式)//所谓资源方式:RAII,创建对象即赋值,撤销即销毁。 return ::operator new(size);//3分配内存
}
//*******main********
#include "template1.h"
void outOfMem()
{
std::cerr<<"Widget outOfMen";
std::abort();
}
int main()
{
Widget::set_new_handler(outOfMem);
Widget* pw = new Widget;
return 0;
}
第三个话题:什么类都可以用这套技术的。复用。
1、mixin base class 实现第二个话题中功能的类作为模板类,基类。事实上那个T并未在定义类的函数中使用。用处请看2。
template<typename T>
class NewHandlerSupport{ //【NewHandlerSupport<T>】
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
//……其他的operator new版本
private:
static std::new_handler currentHandler;
};
class NewHandlerHolder//hold old 资源 【未改变】
{
public:
explicit NewHandlerHolder(std::new_handler nh ):handler(nh){}
~NewHandlerHolder(){std::set_new_handler(handler);}
private:
std::new_handler handler; //记录handler
NewHandlerHolder(const NewHandlerHolder&); //阻止copying
NewHandlerHolder operator=(const NewHandlerHolder&);
};
//*******.cpp******* 【NewHandlerSupport<T>】
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
template<typename T>
std::new_handler NewHandlerSupport<T>::set_new_handler( std::new_handler p ) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
2.CRTP (怪异循环模板 curiously recurring template pattern)——Do It For Me
class Widget:public NewHandlerSupport<Widget>//每一个T一个static 的currentHandler【目的】
{
//和先前一样,但不必声明set_new_handler和operator new,已经继承来了。
//【目的】继承自NewHandlerSupport<Widget>使得Widget类拥有自己的static 的currentHandler。
};
50*什么时候需要手工管理内存?
见书中列举。
一个好的内存管理器。 Booth的Pool程序库——分配大量小型对象。
一个小例子:
static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;
//手工operator new 实现内存前后有标志
void* operator new( std::size_t size)
{
using namespace std;
size_t realSize = size + 2*sizeof(int);//用户所需内存再增加我们两个标志的内存
void* pMem = malloc(realSize);//malloc保证对齐安全
if (!pMem) throw bad_alloc();
//将signature写入内存的最前和最后段落
*( static_cast<int*>(pMem) )= signature;//最前面一个int的内存给了signature用
*( reinterpret_cast<int*>( static_cast<Byte*>(pMem)
+realSize-sizeof(int) ) )= signature;//最后面一个int的内存给了signature用
//返回指针 指向前面sinature后内存位置
return static_cast<Byte*>(pMem) + sizeof(int); //不确定对齐,不安全
}
51*编写operator new和operator delete需要固守的常规。
1、operator new 中应包含一个无限循环,并在其中尝试分配内存。如果成功返回指针,如果失败调用new-handler函数或者抛出异常。
2、operator new 应有能力处理0byte申请。
void* operator new( std::size_t size )
{
using namespace std;
if ( size == 0 )//2、operator new 应有能力处理0byte申请
{
size = 1;
}
while (true)//1、operator new 中应包含一个无限循环,并在其中尝试分配内存。如果成功返回指针,如果失败调用new-handler函数或者抛出异常。
{
尝试分配size byte;
if (分配成功)
return(一个指针,指向分配的来的内存);
//分配失败:找出目前的new-handler函数
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
if (globalHandler)//有new-handler函数
(*globalHandler)()
else//没有
throw std::bad_alloc();
}
}
3、Class专属版本operator new 应处理“比正确大小更大的(错误)申请”
void* BaseClass::operator new(std::size_t size)
{
if (size != sizeof(BaseClass))//如果大小错误
return ::operator new(size);//起用标准new实现
……//否则这里实现
}
写operator new[]时要做的唯一的事:分配一块raw memeory(未加工内存)。
4、operator delete 应在收到null指针时不做任何事。“C++保证删除null指针永远安全”
1 void* operator delete(void* rawMemory)
2 {
3 if (rawMemory ==0 ) return;//如果将被删除的是个null指针,什么都不做
4 //否则这里归还rawMemory所指内存
5 }
5、Class专属版本operator delete应处理“比正确大小更大的(错误)申请”
1 void* BaseClass::operator delete(void* rawMemory,std::size_t size)//比常规的多一个size参数
2 {
3 if (rawMemory ==0 ) return;
4 if (size != sizeof(BaseClass)){//如果大小错误
5 ::operator delete(rawMemory);//起用标准delete实现
6 return;
7 }
8 ……//这里实现归还rawMemory内存
9 return;
10 }
52*placement new和placement delete
placement new是什么:
是除了size那个参数之外,还有其它参数的operator new或者特指其它参数就是void*的operator new。
问题出在:
当调用完new紧接着调用的构造函数抛出异常时,客户代码中的delete无力释放这个new因为不知道指针,C++运行期系统需要调用operator new 对应的operator delete,或者调用参数与placement new对应的placement delete。如果找不到这种特别的delete,C++运行期系统就会什么都不做,那个new就无法被释放。
解决办法是:
提供一个正常的operator delete用于构造期间无任何异常抛出的情况;
提供一个placement delete用于期间有异常抛出的情况,额外参数与placement new一致即可。
第二个话题,placement new和delete掩盖了operator new 和delete的正常版本。什么时候会掩盖呢?比如,placement new 和delete定义在class内部,则这个class就看不到全局域里面常规的new和delete了。又比如派生类内定义的new和delete掩盖了他基类和全局域的所有版本。解决办法是让这个class继承自包含所有标准new和delete形式的类(一共三种,如下),然后用using声明露出其他版本。
1 class StandardNewDeleteForms
2 {
3 public:
4 //normal new/delete
5 static void* operator new(std::size_t size)
6 { return ::operator new(size); }
7 static void operator delete(void* pMemory)
8 { ::operator delete(pMemory); }
9
10 //placement new/delete
11 static void* operator new(std::size_t size, void* ptr)
12 { return ::operator new(size,ptr); }
13 static void operator delete(void* pMemory,void* ptr)
14 { ::operator delete(pMemory,ptr);}
15
16 //nothrow new/delete
17 static void* operator new(std::size_t size,const std::nothrow_t& nt)
18 { return ::operator new(size,nt); }
19 static void* operator delete(void* pMemory,const std::nothrow_t&)
20 { ::operator delete(pMemory); }
21 };
1 class Widget:public StandardNewDeleteForms{
2 public:
3 using StandardNewDeleteForms::operator new;
4 using StandardNewDeleteForms::operator delete;//继承标准形式
5 static void* operator new(std::size_t size,//再加自己的形式
6 std::ostream& logStream);
7 static void operator delete(void* pMemeory,
8 std::ostream& logStream);
9 //...
10 };
原文链接: https://www.cnblogs.com/boldness/archive/2013/02/21/2920460.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/78557
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!