一. 当内存用完时
当找不到足够大的连续的内存块用于存放对象时,一个称为new-handler的函数被调用,或者检查指向函数的指针,如果非0,则那个函数被调用;new-handler的缺省动作是抛出一个异常。通过包含new.h,然后以我们想调用的函数地址为参数,调用set_new_handler()函数,这样就替换了new-handler
#include <iostream>
#include <stdlib.h>
#include <new.h>
using namespace std;
void out_of_memory()
{
cerr << "memory exhausted!" << endl;
exit(1);
}
int main()
{
set_new_handler(out_of_memory);
while(1)
new int[100];
system("pause");
return 1;
}
1. 被new和delete使用的内存分配系统是为通用目的设计的,我们也需要创建、销毁一个特定类的非常多的对象,以至于这个运算成了速度瓶颈
2. 分配不同大小的内存,会导致内存碎片,因此可能还有内存,但是找不到足够大小的连续内存,因此需要定制内存分配器
3. 在嵌入式系统中,要求分配内存花费相同的时间,且不允许出现堆耗尽或出现很多碎片的情况,因此需要定制内存分配器
注释:当我们重载new时,可以改变的只是内存分配部分(delete类似)
三. 重载全局new和delete
#include <stdio.h>
#include <stdlib.h>
void* operator new(size_t sz)
{
printf("operator new: %d bytes\n", sz);
void* m = malloc(sz);
if(!m) puts("out of memory");
return m;
}
void operator delete(void* m)
{
puts("operator delete");
free(m);
}
class s
{
int i[100];
public:
s()
{
puts("s::s();");
}
~s()
{
puts("s::~s();");
}
virtual int count()
{
return 1;
}
};
int main()
{
puts("creating & destorying an int");
int* p = new int(88);
delete p;
puts("creating & destorying an s");
s* S = new s();
delete S;
puts("creating & destorying s[3]");
s* SA = new s[3];
delete []SA;
system("pause");
return 1;
}运行结果:
注释:1. 因为类s有个虚函数,所以对象大小多出4个字节用于存放指向虚函数表的指针 2. 数组大小多出4个字节,用于存放数组的长度等信息 3. 这里使用printf和puts而不是iostream,因为当创建一个iostream对象时(全局cin、cout、cerr),他们会调用new分配内存,会进入死循环,printf不会调用new来初始化自己
四. 为一个类重载new和delete
为一个类重载new和delete时,不必说明是static,它默认为static成员函数
#include <stddef.h>
#include <fstream>
using namespace std;
ofstream out("framis.out");
class framis
{
char c[10];
static unsigned char pool[];
static unsigned char malloc_map[];
public:
enum{PSIZE = 100};
framis()
{
out << "framis()\n";
}
~framis()
{
out << "~framis()\n";
}
void* operator new(size_t);
void operator delete(void*);
};
unsigned char framis::pool[PSIZE * sizeof(framis)];
unsigned char framis::malloc_map[PSIZE] = {0};
void* framis::operator new(size_t)
{
for (int i = 0; i < PSIZE; i++)
{
if (!malloc_map[i])
{
out << "using block " << i << " ...";
malloc_map[i] = 1;
return pool + (i * sizeof(framis));
}
}
out << "out of memory" << endl;
return 0;
}
void framis::operator delete(void* m)
{
if (!m) return;
unsigned long block = (unsigned long)m - (unsigned long)pool;
block /= sizeof(framis);
out << "freeing block " << block << endl;
malloc_map[block] = 0;
}
int main()
{
framis* f[framis::PSIZE];
for (int i = 0; i < framis::PSIZE; i++)
{
f[i] = new framis;
}
new framis; //out of memory
delete f[10];
f[10] = 0;
framis* X = new framis;
delete X;
for (int j = 0; j < framis::PSIZE; j++)
{
delete f[j];
}
system("pause");
return 1;
}
#include <new.h>
#include <fstream>
using namespace std;
ofstream trace("newarray.out");
class widget
{
int i[10];
public:
widget()
{
trace << "*";
}
~widget()
{
trace << "~";
}
void* operator new(size_t sz)
{
trace << "widget::new: " << sz << " bytes" << endl;
return ::new char[sz];
}
void operator delete(void* p)
{
trace << "widget::delete" << endl;
::delete []p;
}
void* operator new[](size_t sz)
{
trace << "widget::new[] : " << sz << " bytes" << endl;
return ::new char[sz];
}
void operator delete[](void* p)
{
trace << "widget::delete[]" << endl;
::delete []p;
}
};
int main()
{
trace << "new widget" << endl;
widget* w = new widget;
trace << "\ndelete widget" << endl;
delete w;
trace << "\nnew widget[25]" << endl;
widget* wa = new widget[25];
trace << "\ndelete widget[25]" << endl;
delete []wa;
system("pause");
return 1;
}六. 对象放置
1. 可能想在内存的指定位置放置一个对象,这对于嵌入式系统特别重要
2. 调用new时,可以从不同的内存分配器中进行选择
这两种需求,都可以通过相同的机制实现,重载运算符new时,带多于一个参数,第一个参数是对象长度,它是在内部计算并由编译器传递。其他的参数可以有我们定义:一个放置对象的地址、一个对内存分配的函数或对象的引用、或其他方便的任何设置
#include <stddef.h>
#include <iostream>
using namespace std;
class X
{
public:
int i;
public:
X(int I = 0)
{
i = I;
}
~X()
{
cout << "X::~X()" << endl;
}
void* operator new(size_t, void* loc)
{
return loc;
}
};
int main()
{
int l[10];
X* xp = new(l) X(88);
cout << xp->i << endl;
xp->X::~X(); //explicit destructor call
system("pause");
return 1;
}
本文详细解析了C++中内存管理的关键概念,包括如何处理内存耗尽情况、重载new和delete函数以优化内存分配、为特定类和数组重载这些操作符、以及对象放置策略在嵌入式系统中的应用。文章还展示了通过重载运算符new和delete来定制内存分配器,以及在不同场景下如何灵活地控制对象的内存布局。
1527

被折叠的 条评论
为什么被折叠?



