应用程序的设计中,我们所说的内存管理就是将系统要处理的内存分配和释放接管过来,内存池是常用的一种设计思路。内存池是在程序的一开始就分配一大块的内存,在后续需要使用内存的地方就直接从内存池中分配出来一块给程序使用,这样就避免了反复的向系统申请和释放内存,从而造成性能上的损失。另一方面,统一的管理还有利于避免内存泄露的出现,因为大量的地方分配内存,容易出现忘记了写delete的情况。要想了解内存管理该怎么做,首先就需要知道C++中给我们提供了哪些东西,我们利用这些东西又可以干什么。
前面两篇中主要讲在系统的各个层面上的内存管理的函数接口,以及它们的使用方式。那些都是系统/Runtime提供给我们的。这一篇中我们就来看看我们可以在这些操作中进行自定义的一些修改。重载是面向对象语言的一个重要的特性。使用重载我们可以在多层继承关系中,让子类能够运行带有自己特色的函数。
1. 调用流程以及可重载部分
首先来看我们在C++中使用内存管理的操作的时候执行的流程,以及这些流程中那些步骤是允许我们去重载的。
上图中,我们在app中使用new来创建一个Foo的对象,这个过程在compiler中会解释成右边的形式,也就是调用operator new函数来分配内存,然后调用构造函数,创建对象。operator new又会去调用下一级的::operator new()函数,这个是一个全局的函数。有些地方也将他们叫做操作符,因为他们和+-*/的重载写法一样,叫法不是我们所要去深究的。这里需要注意:
1. operator new和::operator new都是可以重载的;
2. 如果在类中重载了operator new,就会去调用我们类中的operator new, 然后才是调用到全局的::operator new();
3. 我们也可以在类中的operator new或者全局的operator new中不去调用系统提供的接口,这样这个类就没办法在堆中申请内存了;
理解了上述几点,我们就可以看出,实际上我们重载的这些 相当于是hook了一些操作。
除了在app中直接使用new,我们还可以使用C++提供给我们的allocater, 这也是STL中容器使用的内存管理工具了。下图找你个就是allocator的调用流程。因为allocator是一个提供的工具,所以它里面是直接使用全局的::operator new的。
从上面的两个图中我们可以看出:
- 一旦我们重载了全局的::operator new,那么这个程序中所有的类,以及所有使用new的地方都会走到我们重载的那个全局::operator new中,这个影响是非常大的,也是在程序设计中很少直接这样用的一点。
- 在类中重载operator new, 只会影响这个类的操作,所以一般情况下,这种方式是在内存管理中常用的。
2. 例子
首先,我们来重载全局::operator new 和::operator delete,然后看看是不是如我们之前所说的调用流程一致。
#include <iostream>
using namespace std;
void* myAlloc(size_t size)
{
return malloc(size);
}
void myFree(void* ptr)
{
return free(ptr);
}
inline void* operator new(size_t size)
{
cout << " global new() " << endl;
return myAlloc(size);
}
inline void* operator new[](size_t size)
{
cout << " global new[]() " << endl;
return myAlloc(size);
}
inline void operator delete(void* ptr)
{
cout << " global delete() " << endl;
return myFree(ptr);
}
inline void operator delete[](void* ptr)
{
cout << " global delete[]() " << endl;
return myFree(ptr);
}
int main()
{
int *pA = new int(10);
delete pA;
int* pArr = new int[20];
delete[] pArr;
return 0;
}
验证了全局的重载之后,我们在类中重载operator new和operator delete,看看在类中重载的调用流程是怎样的。
#include <iostream>
using namespace std;
void* myAlloc(size_t size)
{
return malloc(size);
}
void myFree(void* ptr)
{
return free(ptr);
}
// 全局重载new()
inline void* operator new(size_t size)
{
cout << " global new() " << endl;
return myAlloc(size);
}
// 全局重载new[]
inline void* operator new[](size_t size)
{
cout << " global new[]() " << endl;
return myAlloc(size);
}
// 全局重载delete
inline void operator delete(void* ptr)
{
cout << " global delete() " << endl;
return myFree(ptr);
}
// 全局重载delete[]
inline void operator delete[](void* ptr)
{
cout << " global delete[]() " << endl;
return myFree(ptr);
}
class Foo
{
public:
Foo() {
cout << "foo construct" << endl;
}
~Foo() {
cout << "foo deconstruct" << endl;
}
// 类中重载 new
void* operator new(size_t size)
{
cout << "class member new" << endl;
return ::operator new(size);
}
// 类中重载 delete
void operator delete(void* ptr)
{
cout << "class member delete" << endl;
return ::operator delete(ptr);
}
// 类中重载new[]
void* operator new[](size_t size)
{
cout << "class member new[]" << endl;
return ::operator new[](size);
}
// 类中重载 delete[]
void operator delete[](void* ptr)
{
cout << "class member delete[]" << endl;
return ::operator delete[](ptr);
}
};
int main()
{
//int *pA = new int(10);
//delete pA;
//int* pArr = new int[20];
//delete[] pArr;
//Foo *foo = new Foo;
//delete foo;
Foo *foo = new Foo[10];
delete[] foo;
return 0;
}
- 在类中重载operator new[]和operator delete[]
- 重载new() 和 delete()
class Screen {
public:
Screen(int x) :i(x) {};
int geti() { return i; };
void* operator new(size_t size) {
Screen *p;
if (!freeStore) {
size_t chunk = screenChunk * size;
freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
for (; p != &freeStore[screenChunk - 1]; ++p) {
p->next = p + 1;
}
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void operator delete(void* ptr, size_t) {
(static_cast<Screen*>(ptr))->next = freeStore;
freeStore = static_cast<Screen*>(ptr);
}
private:
Screen* next;
static Screen* freeStore;
static const int screenChunk;
int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;